[JAVA] 인터페이스 vs 추상클래스 차이점 정리

2026. 1. 7. 15:10·Language/Java

1. 한눈에 보는 비교 (Interface vs abstract)

인터페이스와 추상 클래스, 막상 비교하려면 헷갈리는 두 개념의 문법적 차이를 표로 정리했습니다.

 

구분 추상 클래스 인터페이스
사용 키워드 abstract interface
사용 가능 변수 제한 없음 static final (상수)
접근 제어자 제한 없음
(public, private, protected, default)
public
사용 가능 메서드 제한 없음 abstract, default, static, private
상속 키워드 extends implements
다중 상속 여부 불가능 가능
클래스에 다중구현, 인터페이스 끼리 다중 상속
공통점 1. 추상 메서드를 가지고 있어야 한다.
2. 인스턴스화 할 수 없다. (new 생성자 사용 불가)
3. 구현체(자식 클래스)의 인스턴스를 사용해야 한다.
4. 상속/구현 받은 클래스는 추상 메서드를 반드시 구현(Override) 해야 한다.

 


2. 인터페이스 (Interface)

인터페이스는 구현 객체가 "같은 동작을 한다"는 것을 보장하기 위한 설계도입니다.

상속 관계에 얽매이지 않고 자유롭게 기능을 장착할 수 있습니다.

핵심 특징

  • 추상화의 극치: 모든 메서드는 기본적으로 public abstract입니다. (Java 8부터 default/static 메서드 가능)
  • 상수만 허용: 일반 변수는 가질 수 없으며, 모든 필드는 public static final 상수입니다.
  • 다중 구현 가능: 클래스는 여러 인터페이스를 동시에 구현(implements)할 수 있어 유연합니다.
  • 행위 중심 (Can-do): "무엇을 할 수 있는가"에 초점을 둡니다. 보통 ~able로 네이밍합니다. (예: Runnable, Serializable)

간단 예시

// '날 수 있는' 기능 정의
interface Flyable {
    void fly(); // public abstract 생략 가능
}

// '수영할 수 있는' 기능 정의
interface Swimmable {
    void swim();
}

// 오리는 날 수도 있고, 수영할 수도 있음 (다중 구현)
class Duck implements Flyable, Swimmable {
    @Override
    public void fly() { System.out.println("오리가 날아갑니다."); }
    
    @Override
    public void swim() { System.out.println("오리가 수영합니다."); }
}

3. 추상 클래스 (Abstract Class)

추상 클래스는 하위 클래스들의 "공통점을 모아둔 미완성 클래스"입니다. 클래스 간의 명확한 계층 구조를 만들 때 사용합니다.

핵심 특징

  • 단일 상속: 일반 클래스와 마찬가지로 하나만 상속(extends)받을 수 있습니다.
  • 일반 멤버 보유: 추상 메서드 외에도 생성자, 일반 변수(필드), 일반 메서드를 가질 수 있습니다.
  • 코드 중복 제거: 공통된 필드와 기능을 부모 클래스에 정의하여 자식 클래스의 코드 중복을 줄입니다.
  • 족보 중심 (Is-a): "무엇인가"에 초점을 둡니다. 서로 연관된 클래스들의 뼈대 역할을 합니다.

간단 예시

// 동물이라는 공통 분모 (미완성 설계도)
abstract class Animal {
    String name; // 상태(변수)를 가질 수 있음

    // 공통 기능 (일반 메서드)
    void eat() { System.out.println("밥을 먹습니다."); }

    // 자식마다 다른 기능 (추상 메서드)
    abstract void sound(); 
}

class Dog extends Animal {
    @Override
    void sound() { System.out.println("멍멍!"); }
}

class Cat extends Animal {
    @Override
    void sound() { System.out.println("야옹!"); }
}

 

5. 그래서 언제, 무엇을 써야 할까?

이론적인 차이점은 알겠지만, 막상 코드를 짤 때 "지금 뭘 써야 하지?" 고민될 때가 많습니다. 상황별 선택 가이드를 제시합니다.

🅰️ 추상 클래스 (Abstract Class)

  • "IS-A" 관계일 때: "강아지는 동물이다", "자동차는 탈것이다"처럼 명확한 계층 구조가 필요할 때.
  • 코드 중복을 줄일 때: 여러 자식 클래스에서 공통으로 사용하는 변수(필드)나 메서드(구현부)가 많을 때.
  • 접근 제어자가 중요할 때: public 이외에 protected나 private 멤버가 필요할 때.

🅱️ 인터페이스 (Interface)

  • "CAN-DO" 관계일 때: "날 수 있다(Flyable)", "저장할 수 있다(Saveable)"처럼 기능을 장착해주고 싶을 때.
  • 서로 다른 조상을 가질 때: 상속 계보와 상관없이 공통 기능을 부여하고 싶을 때. (예: Bird와 Airplane은 다르지만 둘 다 Flyable)
  • 다중 구현이 필요할 때: 하나의 클래스가 여러 가지 역할을 수행해야 할 때.

6. 실전 패턴: 인터페이스와 추상 클래스의 조합 (Mixin)

사실 실무에서는 이 둘을 함께 사용하는 경우가 많습니다. 인터페이스의 "유연함(다중 상속)"과 추상 클래스의 "편리함(중복 제거)"을 모두 챙기는 강력한 설계 패턴입니다.

예시: 게임 캐릭터 만들기 (Game Character)

게임의 모든 캐릭터는 공격(Attack)하고 이동(Move)할 수 있어야 합니다. (인터페이스 역할)
하지만, 모든 캐릭터는 체력(HP)과 레벨(Level)이라는 공통 속성을 가집니다. (추상 클래스 역할)

Step 1. 인터페이스로 설계도(기능) 정의

interface Attackable {
    void attack(); // 모든 캐릭터는 공격해야 함 (강제성)
}

interface Movable {
    void move(); // 모든 캐릭터는 움직여야 함
}

Step 2. 추상 클래스로 공통 멤버(뼈대) 구현

인터페이스에는 변수(상태)를 담을 수 없으니, 추상 클래스를 중간에 둬서 공통 변수를 관리합니다.

// 인터페이스를 구현(implements)하면서, 공통 기능은 미리 만들어두는 추상 클래스
abstract class GameCharacter implements Attackable, Movable {
    // 1. 공통 필드 (중복 제거)
    protected int hp;
    protected int level;

    // 2. 공통 메서드 (중복 제거)
    public void levelUp() {
        this.level++;
        System.out.println("레벨 업! 현재 레벨: " + this.level);
    }
    
    // attack()과 move()는 자식마다 다르므로 추상 메서드로 남겨둠 (구현 강제)
}

Step 3. 실제 클래스 구현 (단순화)

이제 실제 캐릭터 클래스들은 hp, level 변수를 일일이 선언할 필요 없이, 핵심 로직에만 집중하면 됩니다.

class Warrior extends GameCharacter {
    @Override
    public void attack() {
        System.out.println("칼로 공격합니다!");
    }
    @Override
    public void move() {
        System.out.println("뛰어서 이동합니다.");
    }
}

class Wizard extends GameCharacter {
    @Override
    public void attack() {
        System.out.println("마법 불꽃 발사!");
    }
    @Override
    public void move() {
        System.out.println("텔레포트로 이동합니다.");
    }
}

7. 결론: 다형성(Polymorphism)의 중요성

우리가 인터페이스와 추상 클래스를 조합해서 복잡하게 설계한 진짜 이유는 바로 "다형성을 200% 활용하기 위해서"입니다.

이제 우리는 전사(Warrior)든 마법사(Wizard)든 상관없이, 모두 GameCharacter라는 하나의 타입으로 묶어서 관리할 수 있습니다.

다형성 활용 코드 예시

public class GameMain {
    public static void main(String[] args) {
        // 1. 다형성: 서로 다른 객체를 '부모 타입'으로 묶음
        List<GameCharacter> party = new ArrayList<>();
        party.add(new Warrior());
        party.add(new Wizard());

        // 2. 일괄 처리: 구체적인 타입(전사인지 마법사인지)을 몰라도 됨
        for (GameCharacter character : party) {
            character.move();   // 공통 기능 (추상 클래스에서 상속)
            character.attack(); // 개별 기능 (오버라이딩된 메서드 실행)
            // 전사는 "칼 공격", 마법사는 "마법 공격"이 자동으로 나감!
        }
    }
}

 

  이 패턴(Interface + Abstract)의 장점 요약

  • - 설계의 표준화 (Interface): "모든 캐릭터는 반드시 공격해야 한다"는 규칙을 강제하여, 개발자가 실수로 기능을 누락하는 것을 방지합니다.
  • -  코드 중복 제거 (Abstract Class): 체력(HP), 레벨(Level) 처럼 모든 캐릭터가 갖는 공통 코드를 부모 클래스 한곳에서만 관리하면 되므로 유지보수가 매우 쉬워집니다.
  • -  확장성 (Polymorphism): 나중에 Archer(궁수) 클래스가 추가되어도, 기존의 GameMain 코드는 한 줄도 수정할 필요가 없습니다. 그냥 리스트에 추가만 하면 됩니다. (OCP 원칙 준수)

 

자바의 꽃은 객체지향이고, 객체지향의 완성은 다형성입니다.
인터페이스로 "큰 그림(설계)"을 그리고, 추상 클래스로 "공통 분모(구현)"를 묶어주는 이 패턴을 익혀두면, 좋을 것 같습니다.

 


📚 Reference & Source

이 포스팅은 "요즘 공부 도와주는 갓 인파"님의 블로그 내용을 바탕으로 정리했습니다.
더 깊이 있는 내용과 예제는 아래 원문에서 확인하실 수 있습니다.

🔗 [Inpa Dev 👨‍💻] 자바 인터페이스 vs 추상클래스 차이점 완벽 이해하기

'Language > Java' 카테고리의 다른 글

[JAVA] Stack 구현 & Stack Class의 문제점 (feat. Deque)  (0) 2025.12.19
[JAVA] Collection Framework & Collections Class _ Part 1  (0) 2025.12.16
[JAVA] Getter/Setter 이중성 & 문제점 Refactoring  (1) 2025.12.12
[JAVA] 접근제한자 & 캡슐화  (0) 2025.12.12
[JAVA] 객체 지향 프로그래밍 4대 원칙  (0) 2025.12.12
'Language/Java' 카테고리의 다른 글
  • [JAVA] Stack 구현 & Stack Class의 문제점 (feat. Deque)
  • [JAVA] Collection Framework & Collections Class _ Part 1
  • [JAVA] Getter/Setter 이중성 & 문제점 Refactoring
  • [JAVA] 접근제한자 & 캡슐화
hlxecz
hlxecz
1인분 개발자가 되고 싶은 개발 기록
  • hlxecz
    H.Dev Log
    hlxecz
  • 전체
    오늘
    어제
    • 분류 전체보기 (12)
      • Language (7)
        • Java (7)
        • C (0)
        • PHP (0)
        • Python (0)
      • Framewrok (0)
        • Spring (0)
      • Data (0)
        • DBMS (0)
      • Cloud (0)
        • Amazon Cloud (0)
      • Knowledge (3)
        • 자료구조 (0)
        • 알고리즘 (1)
        • 디자인 패턴 (2)
      • DevOps (0)
      • Dev Kit (0)
        • InteliJ (0)
        • VSCode (0)
      • TEST (2)
        • Testing (2)
        • Error (0)
      • ETC (0)
        • 일상 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Solid
    Collection
    알고리즘
    BIG-O
    자바
    Interface
    재귀함수
    setter
    디자인 패턴
    피드백
    문제점
    OOP
    인터페이스 vs 추상클래스
    GOF
    collections
    Java
    캡슐화
    생각정리
    string
    getter
    시간복잡도
    Stack
    Abstract
    바밀로니아
    인터페이스
    문제풀이
    빅오표기법
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.5
hlxecz
[JAVA] 인터페이스 vs 추상클래스 차이점 정리
GitHub 상단으로

티스토리툴바