전체 목록
JavaMedium#54

추상 클래스(Abstract Class)와 인터페이스(Interface)의 차이점을 설명해주세요.

#Java#OOP#인터페이스#추상클래스
힌트

다중 상속, 상태(필드) 보유 여부, default 메서드를 생각해보세요.

정답 및 해설

추상 클래스(Abstract Class)와 인터페이스(Interface)의 차이점을 설명해주세요.

추상 클래스와 인터페이스는 Java에서 추상화를 구현하는 두 가지 핵심 메커니즘입니다. 둘 다 직접 인스턴스화할 수 없고 하위 타입이 구현을 제공해야 한다는 공통점이 있지만, 설계 목적과 기능에서 중요한 차이가 있습니다.

추상 클래스 (Abstract Class)

abstract 키워드로 선언하며, IS-A 관계를 표현할 때 사용합니다. 공통 구현을 공유하는 클래스 계층을 만들 때 적합합니다.

핵심 특징

  • 단일 상속만 가능 (extends 키워드)
  • 인스턴스 변수(상태) 보유 가능
  • 생성자 정의 가능
  • abstract 메서드와 일반 메서드 혼용 가능
  • private, protected, public모든 접근 제어자 사용 가능
public abstract class Animal {
    private String name;  // 인스턴스 변수
    private int age;

    // 생성자
    public Animal(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 추상 메서드 — 하위 클래스가 반드시 구현
    public abstract void makeSound();

    // 일반 메서드 — 공통 구현 공유
    public void breathe() {
        System.out.println(name + "이(가) 숨을 쉽니다.");
    }

    public String getName() { return name; }
}

public class Dog extends Animal {
    public Dog(String name, int age) {
        super(name, age); // 부모 생성자 호출
    }

    @Override
    public void makeSound() {
        System.out.println("멍멍!");
    }
}

인터페이스 (Interface)

interface 키워드로 선언하며, CAN-DO 관계를 표현합니다. 어떤 능력이나 계약(contract)을 정의할 때 적합합니다.

핵심 특징

  • 다중 구현 가능 (implements 키워드)
  • 상수(public static final)만 가능 (Java 8+ 이전)
  • 생성자 없음
  • Java 8+: default 메서드, static 메서드 추가 가능
  • Java 9+: private 메서드 추가 가능
public interface Flyable {
    int MAX_ALTITUDE = 10000; // 암묵적으로 public static final

    void fly(); // 암묵적으로 public abstract

    // Java 8+ default 메서드
    default void land() {
        System.out.println("착지합니다.");
    }

    // Java 8+ static 메서드
    static boolean canFlyInStorm() {
        return false;
    }
}

public interface Swimmable {
    void swim();
}

// 다중 인터페이스 구현
public class Duck extends Animal implements Flyable, Swimmable {
    public Duck(String name) {
        super(name, 1);
    }

    @Override
    public void makeSound() { System.out.println("꽥꽥!"); }

    @Override
    public void fly() { System.out.println("날아오릅니다!"); }

    @Override
    public void swim() { System.out.println("수영합니다!"); }
}

Java 8 이후 변화

Java 8에서 default 메서드가 도입되면서 인터페이스도 구현을 가질 수 있게 되었습니다. 이로 인해 두 개념의 차이가 좁혀졌지만, 상태(인스턴스 변수)가 필요하다면 여전히 추상 클래스를 사용해야 합니다.

public interface Loggable {
    // default 메서드로 공통 구현 제공
    default void log(String message) {
        System.out.println("[" + getClass().getSimpleName() + "] " + message);
    }

    // 구현 클래스가 오버라이드할 수 있음
    default String getLogPrefix() {
        return "INFO";
    }
}

언제 무엇을 사용할까?

추상 클래스를 선택하는 경우

  • IS-A 관계를 나타낼 때 (Dog is an Animal)
  • 여러 클래스가 **공통 상태(필드)**를 공유해야 할 때
  • 하위 클래스에서 공통 구현 코드를 재사용해야 할 때
  • protected 접근 제어가 필요한 경우

인터페이스를 선택하는 경우

  • CAN-DO 관계를 나타낼 때 (Duck can fly, can swim)
  • 다중 구현이 필요한 경우
  • 타입의 계층 구조와 무관하게 능력/계약을 정의할 때
  • 서로 관련 없는 클래스들에게 공통 기능을 부여할 때

비교 정리

구분추상 클래스인터페이스
키워드abstract classinterface
상속/구현extends (단일)implements (다중)
인스턴스 변수가능불가 (상수만)
생성자가능불가
메서드 구현가능 (일반+추상)Java 8+: default/static
접근 제어자모두 가능public (암묵적)
관계 표현IS-ACAN-DO
상태 보유가능불가
// 실제 활용 예시
abstract class Vehicle {       // IS-A: 차량이라는 공통 상태와 동작
    protected int speed;
    protected String model;

    public abstract void accelerate();

    public void brake() {
        speed = 0;
        System.out.println("정지했습니다.");
    }
}

interface Electric {           // CAN-DO: 전기 능력
    void charge();
    int getBatteryLevel();
}

interface Autonomous {         // CAN-DO: 자율주행 능력
    void enableAutopilot();
}

// 추상 클래스 상속 + 다중 인터페이스 구현
class TeslaCar extends Vehicle implements Electric, Autonomous {
    private int batteryLevel;

    @Override
    public void accelerate() { speed += 30; }

    @Override
    public void charge() { batteryLevel = 100; }

    @Override
    public int getBatteryLevel() { return batteryLevel; }

    @Override
    public void enableAutopilot() { System.out.println("자율주행 활성화"); }
}