- 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다.
- 재정의 가능한 메서드 => public과 protected 메서드 중 final이 아닌 모든 메서드
- 더 넓게 말하면, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다. 예를 들어 백그라운드 스레드나 정적 초기화 과정에서도 호출이 일어날 수 있다.
- 내부 메커니즘을 문서로 남기는 것만이 상속을 위한 설계의 전부는 아니다.
- 효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅(hook)을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다. (드물게는 protected 필드로 공개해야 할 수도 있다.)
- 상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어보는 것이 '유일'하다.
- 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.
- 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
- 잘못된 예시
public class Super {
// 잘못된 예 - 생성자가 재정의 가능 메서드를 호출
public Super() { overrideMe(); }
public void overrideMe() { }
}
public final class Sub extends Super {
// 초기화되지 않은 final 필드. 생성자에서 초기화한다.
private final Instant instant;
Sub() {
instant = Instant.now();
}
// 재정의 가능 메서드. 상위 클래스의 생성자가 호출한다.
@Override
public void overrideMe() {
System.out.println(instant);
}
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}
}
- 이 프로그램이 'instant'를 두 번 출력하리라 기대했겠지만, 첫 번째는 null을 출력한다. => 상위 클래스의 생성자는 하위 클래스의 생성자가 인스턴스 필드를 초기화하기도 전에 overrideMe를 호출하기 때문이다.
- 상속용 클래스에서 Cloneable이나 Serializable을 구현할지 정해야 한다면, 이들을 구현할 때 따르는 제약도 생성자와 비슷하다는 점에 주의하자. => clone과 readObject 메서드는 생성자와 비슷한 효과를 낸다(새로운 객체를 만든다).
- 즉, clone과 readObject 모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
- Serializable을 구현한 상속용 클래스가 readResolve나 writeReplace 메서드를 갖는다면 이 메서드들은 private이 아닌 protected로 선언해야 한다. => private으로 선언한다면 하위 클래스에서 무시되기 때문이다.
- 추상 클래스나 인터페이스의 골격 구현(아이템 20)처럼 상속을 허용하는 게 명백히 정당한 상황이 있고, 불변 클래스(아이템 17)처럼 명백히 잘못된 상황이 있다.
- 그렇다면 그 외의 일반적인 구체 클래스는 어떨까? => 상속용으로 설계하지 않은 클래스는 상속을 금지한다.
* 상속을 금지하는 방법
1. 클래스를 final로 선언한다.
2. 모든 생성자를 private이나 package-private으로 선언하고 public 정적 팩터리를 만들어준다.
[핵심 정리]
상속용 클래스를 설계하기란 결코 만만치 않다. 클래스 내부에서 스스로를 어떻게 사용하는지(자기사용 패턴) 모두 문서로 남겨야 하며, 일단 문서화한 것은 그 클래스가 쓰이는 한 반드시 지켜야 한다. 그러니 클래스를 확장해야 할 명확한 이유가 떠오르지 않으면 상속을 금지하는 편이 나을 것이다.
'공부 기록 > Java' 카테고리의 다른 글
[JPA] 7장 - 고급 매핑(1) - 상속 관계 매핑 (1) | 2023.11.22 |
---|---|
[이펙티브 자바 3/E] 아이템20 - 추상 클래스보다는 인터페이스를 우선하라 (1) | 2023.11.13 |
[JPA] 6장 - 다양한 연관관계 매핑(2) - 일대일, 다대다 (0) | 2023.09.15 |
[이펙티브 자바 3/E] 아이템18 - 상속보다는 컴포지션을 사용하라 (0) | 2023.09.08 |
[이펙티브 자바 3/E] 아이템17 - 변경 가능성을 최소화하라 (0) | 2023.09.07 |