이펙티브 자바, 쉽게 정리하기 - item 20. 추상 클래스보다는 인터페이스를 우선하라
추상 클래스와 인터페이스 살펴보기
공통점
- 메서드의 시그니쳐만 만들고 구현을 구현 클래스에게 맡긴다.
차이
추상클래스
: 단일 상속만 가능하다. 구현체는 추상클래스의 하위 클래스가 된다.인터페이스
: 다중 상속이 가능하다. 인터페이스를 구현했다면, 같은 타입으로 취급된다.
인터페이스의 장점 활용하기 1: 믹스인(mixin)
믹스인
: 구현 클래스에 '선택적 행위'를 제공한다고 선언하는 효과를 준다.- ex)
Comparable
,Iterable
,AutoCloseable
,Serializable
- ex)
추상 클래스는 이미 다른 클래스를 상속하는 클래스의 경우, 해당 클래스가 두 부모 클래스를 가질 수는 없으므로 믹스인으로 사용될 수 없다.
인터페이스의 장점 활용하기 2: 계층이 없는 타입 프레임워크
public class Item20Test {
interface Singer {
void Sing();
}
interface Songwriter {
void compose(int chartPosition);
}
interface SingerSongWriter extends Singer, Songwriter {
void strum();
}
}
SingerSongWriter
인터페이스처럼 인터페이스는 다른 인터페이스를 상속할 수 있다.- 인터페이스의 상속은 상속이라는 단어는 사용하지만, 클래스의 상속처럼 부모, 자식 계층이 존재하지 않는다.
- 부모 클래스의 생성자를 호출할 필요 없다.
- 부모 클래스의 구현 내용도 이어받지 않는다.
- 정의된 메서드들만 구현하면 된다.
- 클래스로 이와 같은 구조를 구현하려면, 상하 관계를 따져보며 차례로 단일 상속을 받아야 한다.
- 만든 이후에도 클래스 상속이 갖는 여러가지 제약을 갖게 된다.
인터페이스의 장점 활용하기 3: 래퍼(Wrapper) 클래스
- 래퍼 클래스란 기존에 인터페이스를 구현한 클래스를 주입받아 기존 구현체에 부가기능을 손쉽게 더할 수 있는 클래스다.
- 이를
데코레이터 패턴(Decorator Pattern)
이라고 한다. 컴포지션
(다른 클래스를 사용하는 패턴)과전달
(인터페이스 구현체를 주입)을 합쳐위임(delegation)
이라고 부른다.
- 이를
인터페이스 장점 활용하기 4: 디폴트 메서드 (default method)
- 인터페이스의 메서드 중 구현 방법이 명확한 메서드가 있다면, 디폴트 메서드를 활용할 수 있다.
- 단,
equals()
,hashCode()
와 같이Object
에서 제공하는 메서드는 디폴트 메서드로 제공해선 안 된다. - 단,
public
이 아닌 정적 멤버도 가질 수 없다.
- 단,
/**
* Removes all of the elements of this collection that satisfy the given
* predicate. Errors or runtime exceptions thrown during iteration or by
* the predicate are relayed to the caller.
*
* @implSpec
* The default implementation traverses all elements of the collection using
* its {@link #iterator}. Each matching element is removed using
* {@link Iterator#remove()}. If the collection's iterator does not
* support removal then an {@code UnsupportedOperationException} will be
* thrown on the first matching element.
*
* @param filter a predicate which returns {@code true} for elements to be
* removed
* @return {@code true} if any elements were removed
* @throws NullPointerException if the specified filter is null
* @throws UnsupportedOperationException if elements cannot be removed
* from this collection. Implementations may throw this exception if a
* matching element cannot be removed or if, in general, removal is not
* supported.
* @since 1.8
*/
default boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter);
boolean removed = false;
final Iterator<E> each = iterator();
while (each.hasNext()) {
if (filter.test(each.next())) {
each.remove();
removed = true;
}
}
return removed;
}
- 위는
Collection
인터페이스 내부의removeIf()
의 예인데, 위와 같이@implSpec
을 이용해 상속하려는 사람을 위한 설명을 문서화해두는 것이 좋다.
골격 구현(템플릿 메서드 패턴) 활용
- 인터페이스로는 타입 정의 및 몇가지 디폴트 클래스를 구현한다.
- 골격 구현 추상 클래스로 나머지 메서드들까지 구현한다.
static List<Integer> intArrayAsList(int[] a) {
Objects.requireNonNull(a);
return new AbstractList<>() {
@Override
public Integer get(int i) {
return a[i];
}
@Override
public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val;
return oldVal;
}
@Override
public int size() {
return a.length;
}
}
}
- 이 코드는 골격 구현의 구체 클래스이다.
- 사용자는 몇가지 오버라이딩 메서드만을 통해 원하는 바를 구현했다.
int
배열을 받아Integer
인스턴스의 리스트 형태로 보여주는 어댑터이기도 하다.- 박싱과 언박싱 덕에 성능은 그리 좋지 않다.
- 익명 클래스를 사용했다.
골격 구현 클래스는 추상 클래스처럼 구현을 도와주는 동시에 추상 클래스로 타입을 정의할 때 따라오는 심각한 제약에서는 자유롭다. 인터페이스 디폴트 메서드가 갖는 한계를 추상클래스를 이용해서 벗어난다.
인터페이스를 구현한 클래스에서 골격 구현을 확장한
private
내부 클래스를 정의하고, 각 메서드 호출을 내부 클래스의 인스턴스에 전달하여 활용할 수도 있다. 이는 래퍼 클래스에서의 활용법과 비슷하다. 이 방식을 시뮬레이트한 다중 상속이라 하고, 다중 상속의 많은 장점을 제공하며 동시에 단점은 피하게 해준다.
단순 구현(simple implementation)
- 단순 구현은 골격 구현의 작은 변종이다.
- 상속을 위해 인터페이스를 구현했지만, 추상 클래스가 아니란 점이 차이이다.
- 이름 그대로 동작하는 가장 간단한 구현이다.
- 그대로 써도 되고 필요에 따라 확장해도 무방하다.
AbstractMap.SimpleEntry
클래스를 참고할 수 있다.
핵심 정리
- 다중 구현용 타입으로는 인터페이스가 가장 적당하다.
- 복잡한 인터페이스라면 구현하는 수고를 덜어주는 골격 구현을 함께 제공해보자.
- 골격 구현은 '가능한 한' 인터페이스의 디폴트 메서드로 제공하여 인터페이스를 구현한 모든 곳에서 활용하도록 하자.
- 사실 인터페이스의 구현상 제약 때문에 골격 구현을 추상 클래스로 제공하는 경우가 사실 더 흔하다.
'Java > 이펙티브 자바' 카테고리의 다른 글
이펙티브 자바, 쉽게 정리하기 - item 22. 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2022.01.04 |
---|---|
이펙티브 자바, 쉽게 정리하기 - item 21. 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2022.01.04 |
이펙티브 자바, 쉽게 정리하기 - item 19. 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라. (0) | 2022.01.04 |
이펙티브 자바, 쉽게 정리하기 - item 18. 상속보다는 컴포지션을 사용하라 (2) | 2022.01.04 |
이펙티브 자바, 쉽게 정리하기 - item 17. 변경 가능성을 최소화하라 (0) | 2022.01.03 |