아이템 20. 추상 클래스보다는 인터페이스를 우선하라.
1. 인터페이스의 장점
인터페이스는 다중 구현(다중 상속)이 가능하다.
클래스는 하나만 상속 가능하지만, 인터페이스는 여러 개를 동시에 구현할 수 있다.
인터페이스는 믹스인(mixin) 역할을 수행할 수 있다.
특정 기능 집합을 기존 계층과 관계없이 추가할 수 있는 방식이다.
인터페이스는 기존 클래스와 관계없이 구현 가능하다.
어떤 클래스도 상속하지 않고, 외부 라이브러리 클래스에도 인터페이스를 추가 구현할 수 있다.
2. 추상 클래스의 한계
추상 클래스는 단일 상속만 가능하다.
이미 다른 클래스를 상속 중이라면 사용할 수 없다.
추상 클래스는 구현 클래스와 강하게 결합된다.
상속 구조 안에서만 의미가 있으며, 외부에 기능을 얹기 어렵다.
추상 클래스는 상속을 통한 확장을 전제로 하므로 더 유연한 설계가 어려울 수 있다.
3. 디폴트 메서드를 이용한 인터페이스 진화
java 8 부터 인터페이스에 default 메서드를 정의할 수 있으므로, 인터페이스도 구현을 가질 수 있게 되었다.
이를 통해 기존 인터페이스에 메서드를 추가할 수 있게 되었고, 기존 구현체를 깨뜨리지 않고 기능을 확장할 수 있게 되었다.
단, default 메서드는 신중히 써야 한다.
상속 충돌, 의미 없는 기본 구현 등 부작용이 있을 수 있다.
4. 골격 구현 클래스(Skeletal Implementation) 패턴
복잡한 인터페이스를 만들 때, 일부 기본 구현을 제공하고자 하면 골격 구현 클래스를 함께 제공할 수 있다.
이는 추상 클래스와 인터페이스를 조합해서 사용하는 방식이다.
인터페이스는 계약(contract)을 제공하고,
추상클래스는 선택적 구현(skeleton)을 제공하는 형태로 함께 쓰면 유용하다.
5. 인터페이스 상속 계층이 추상 클래스보다 유연하다.
인터페이스는 여러 개를 상속받을 수 있으며, 계층 구조를 자유롭게 만들 수 있다.
추상 클래스는 단일 계층 구조만 가능하고, 기능 분리를 어렵게 만든다.
역할(behavior) 단위로 재사용 가능한 설계는 인터페이스가 더 적합하다.
6. 결론 : 가능한 한 인터페이스를 먼저 고려하라
새로운 타입을 설계할 때는 추상 클래스보다 인터페이스를 우선 고려해야 한다.
인터페이스는 유연하고 다중 구현이 가능하며, 기존 구조와 무관하게 적용할 수 있다.
추상 클래스는 골격 구현(skeletal implementation) 이 필요할 때만 보조적으로 사용하라.
디폴트 메서드는 유용하지만, 기본 구현이 진짜 의미 있는 동작일 때만 사용해야 한다.
7. 심화 / 느낀점
계속 똑같은 얘기지만, 상속보다 구현이 결합도가 훨~씬 낮다.
디폴트 메서드 충돌 상황
다중 인터페이스 구현 시 디폴트 메서드 충돌이 발생하면, 반드시 재정의해야 하며 명시적으로는 어느 인터페이스의 구현을 따를 지 지정해야 한다.
Kotlin의 인터페이스와 다중 상속
Kotlin의 인터페이스도 Java처럼 기본 구현 가능(default 와 유사)
다중 상속 구조도 명시적으로 super
.method() 로 해결 가능함.
Kotlin 에서는 데이터 모델과 역할 분리를 위해 인터페이스 사용이 더 일반화 되어 있음.
실무에서 추상 클래스가 더 적절한 경우
인터페이스보다 상태(state) 를 함께 관리해야 할 때 추상 클래스가 필요하다.
ex) 스레드풀, 캐시 정책 등의 공통 상태와 메서드를 함께 다룰 때