Yomni's TIL Help

아이템 20. 추상 클래스보다는 인터페이스를 우선하라.

1. 인터페이스의 장점

  • 인터페이스는 다중 구현(다중 상속)이 가능하다.

    • 클래스는 하나만 상속 가능하지만, 인터페이스는 여러 개를 동시에 구현할 수 있다.

  • 인터페이스는 믹스인(mixin) 역할을 수행할 수 있다.

    • 특정 기능 집합을 기존 계층과 관계없이 추가할 수 있는 방식이다.

  • 인터페이스는 기존 클래스와 관계없이 구현 가능하다.

    • 어떤 클래스도 상속하지 않고, 외부 라이브러리 클래스에도 인터페이스를 추가 구현할 수 있다.

2. 추상 클래스의 한계

  • 추상 클래스는 단일 상속만 가능하다.

    • 이미 다른 클래스를 상속 중이라면 사용할 수 없다.

  • 추상 클래스는 구현 클래스와 강하게 결합된다.

    • 상속 구조 안에서만 의미가 있으며, 외부에 기능을 얹기 어렵다.

  • 추상 클래스는 상속을 통한 확장을 전제로 하므로 더 유연한 설계가 어려울 수 있다.

3. 디폴트 메서드를 이용한 인터페이스 진화

  • java 8 부터 인터페이스에 default 메서드를 정의할 수 있으므로, 인터페이스도 구현을 가질 수 있게 되었다.

public interface Singer { void sing(); default void rest() { System.out.println("Taking a break..."); } }
  • 이를 통해 기존 인터페이스에 메서드를 추가할 수 있게 되었고, 기존 구현체를 깨뜨리지 않고 기능을 확장할 수 있게 되었다.

  • 단, default 메서드는 신중히 써야 한다.

    • 상속 충돌, 의미 없는 기본 구현 등 부작용이 있을 수 있다.

4. 골격 구현 클래스(Skeletal Implementation) 패턴

  • 복잡한 인터페이스를 만들 때, 일부 기본 구현을 제공하고자 하면 골격 구현 클래스를 함께 제공할 수 있다.

  • 이는 추상 클래스와 인터페이스를 조합해서 사용하는 방식이다.

public interface Map<K, V> { int size(); boolean isEmpty(); // ... }
public abstract class AbstractMap<K, V> implements Map<K, V> { public boolean isEmpty() { return size() == 0; } // 일부 기본 구현 제공 }
  • 인터페이스는 계약(contract)을 제공하고,


    추상클래스는 선택적 구현(skeleton)을 제공하는 형태로 함께 쓰면 유용하다.

5. 인터페이스 상속 계층이 추상 클래스보다 유연하다.

  • 인터페이스는 여러 개를 상속받을 수 있으며, 계층 구조를 자유롭게 만들 수 있다.

  • 추상 클래스는 단일 계층 구조만 가능하고, 기능 분리를 어렵게 만든다.

  • 역할(behavior) 단위로 재사용 가능한 설계는 인터페이스가 더 적합하다.

6. 결론 : 가능한 한 인터페이스를 먼저 고려하라

  • 새로운 타입을 설계할 때는 추상 클래스보다 인터페이스를 우선 고려해야 한다.

  • 인터페이스는 유연하고 다중 구현이 가능하며, 기존 구조와 무관하게 적용할 수 있다.

  • 추상 클래스는 골격 구현(skeletal implementation) 이 필요할 때만 보조적으로 사용하라.

  • 디폴트 메서드는 유용하지만, 기본 구현이 진짜 의미 있는 동작일 때만 사용해야 한다.

7. 심화 / 느낀점

  • 계속 똑같은 얘기지만, 상속보다 구현이 결합도가 훨~씬 낮다.

디폴트 메서드 충돌 상황

  • 다중 인터페이스 구현 시 디폴트 메서드 충돌이 발생하면, 반드시 재정의해야 하며 명시적으로는 어느 인터페이스의 구현을 따를 지 지정해야 한다.

interface A { default void hello() { System.out.println("Hello from A"); } } interface B { default void hello() { System.out.println("Hello from B"); } } class C implements A, B { @Override public void hello() { A.super.hello(); // 선택해서 명시적으로 호출 } }

Kotlin의 인터페이스와 다중 상속

  • Kotlin의 인터페이스도 Java처럼 기본 구현 가능(default 와 유사)

  • 다중 상속 구조도 명시적으로 super

    .method() 로 해결 가능함.

  • Kotlin 에서는 데이터 모델과 역할 분리를 위해 인터페이스 사용이 더 일반화 되어 있음.

실무에서 추상 클래스가 더 적절한 경우

  • 인터페이스보다 상태(state) 를 함께 관리해야 할 때 추상 클래스가 필요하다.

    • ex) 스레드풀, 캐시 정책 등의 공통 상태와 메서드를 함께 다룰 때

Last modified: 08 May 2025