본문은 Effective Java를 읽고 간단하게 정리한 글입니다. 필요에 따라 생략/수정된 부분이 있을 수 있으며, 내용이 추후 변경될 수 있습니다.
선결론
- 일반적으로 다중 구현용 타입엔 인터페이스가 추상 클래스보다 적합하다
- 인터페이스를 작성할 땐 골격 구현을 함께 제공하는 방법을 고려하자
인터페이스 장점
- 기존 클래스에도 손쉽게 새로운 인터페이스를 구현해넣을 수 있다
- 기존 클래스 위에 새로운 추상 클래스를 끼워넣기는 일반적으로 어렵다
- 인터페이스는 메서드를 추가하고, 클래스 선언에 implements 구문만 추가하면 된다
- 믹스인(mix) 정의에 안성맞춤이다
- 믹스인이란 클래스가 구현할 수 있는 타입으로, 대상 타입의 주된 기능에 선택적 기능을 혼합(mixed in)한다는 의미이다
- Comparable이 대표적인 믹스인 인터페이스이다
- 계층구조가 없는 타입 프레임워크를 만들 수 있다
- 인터페이스는 다중상속이 가능하고, 새로운 메서드를 추가하는 것도 가능하여 유연하다
- 디폴트 메서드를 제공하여 반복되는 코드의 작성을 줄일 수 있다
- 메서드 중 구현 방법이 명백한 것이 있다면 그 구현을 디폴트 메서드로 제공한다
- 이때, @impleSpec 자바독 태그를 붙여 문서화해야 한다(아이템 19)
- 인터페이스와 추상 골격 구현(skeleton implementation) 클래스를 함께 제공하는 식으로 인터페이스와 추상 클래스의 장점을 모두 취할 수 있다
- 인터페이스로는 타입을 정의하고 필요한 디폴트 메서드를 함께 제공하고, 골격 구현 클래스는 나머지 메서드들까지 구현한다
- 이것이 바로 템플릿 메서드 패턴이다
골격 구현 작성
- 1. 인터페이스에서 다른 메서드들의 구현에 사용되는 기반 메서드들을 선정한다
- 이 기반 메서드들은 골격 구현에서는 추상 메서드가 된다
- 2. 위에서 선정한 기반 메서드들을 사용해 직접 구현할 수 있는 메서드를 모두 디폴트 메서드로 제공한다
- 단, equals와 hashCode 같은 Object의 메서드는 디폴트 메서드로 제공하면 안 된다
즉, 인터페이스 -> 골격 구현 클래스(추상 클래스) -> 구체 클래스의 구조를 가지며, 각 계층은 다음의 역할을 따른다
- 인터페이스에서는 사용할 메서드를 정의하고, 기반 메서드를 선정한 후 이를 묶어 디폴트 메서드로 제공한다
- 하나의 작업을 수행하는 데 필요한 메서드들과, 실행순서를 정해서 공통의 메서드로 묶는다
- 추상 클래스에서는 구체 클래스에서 공통적으로 사용되는 메서드들을 재정의하여 중복을 제거한다
- 메서드의 구현이 중복되는 부분을 재정의한다
- 구체 클래스에서는 기반 메서드를 재정의한다
- 공통적인 작업의 순서(인터페이스)와 구현(추상 클래스)이 채워졌으니, 개별적으로 동작할 메서드를 재정의해주면 된다
interface Entry<K, V> {
K getKey();
V getValue();
V setValue(V value);
default String getKeyAndValueToString() {
return getKey().toString() + getValue().toString();
}
}
abstract class AbstractMapEntry<K, V> implements Entry {
@Override
public Object setValue(Object value) {
throw new UnsupportedOperationException();
}
@Override
public int hashCode() {
return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue());
}
@Override
public String toString() {
return getKey() + "=" + getValue();
}
@Override
public boolean equals(Object obj) {
// ...
}
}
class ConcreteMapEntry<K, V> extends AbstractMapEntry<K, V> implements Entry {
@Override
public Object getKey() {
// ...
}
@Override
public Object getValue() {
// ...
}
}
'책 > Effective Java' 카테고리의 다른 글
[이펙티브 자바] 아이템 22: 인터페이스는 타입을 정의하는 용도로만 사용하라 (0) | 2022.05.11 |
---|---|
[이펙티브 자바] 아이템 21: 인터페이스는 구현하는 쪽을 생각해 설계하라 (0) | 2022.05.09 |
[이펙티브 자바] 아이템 19: 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 (0) | 2022.05.05 |
[이펙티브 자바] 아이템 18: 상속보다는 컴포지션을 사용하라 (0) | 2022.05.05 |
[이펙티브 자바] 아이템 17: 변경 가능성을 최소화하라 (0) | 2022.05.03 |