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 class | interface |
| 상속/구현 | extends (단일) | implements (다중) |
| 인스턴스 변수 | 가능 | 불가 (상수만) |
| 생성자 | 가능 | 불가 |
| 메서드 구현 | 가능 (일반+추상) | Java 8+: default/static |
| 접근 제어자 | 모두 가능 | public (암묵적) |
| 관계 표현 | IS-A | CAN-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("자율주행 활성화"); }
}