좋은 API 문서는 어떻게가 아닌 무엇을 하는지를 설명해야 하지만, 상속이 캡슐화를 해치므로 내부 구현 방식을 설명해야한다.
API 문서의 메서드 설명 끝에 "Implementation Requirements”로 시작하는 절을 볼 수 있는데, 그 메서드의 내부 동작 방식을 설명하는 곳.
@implSpec 태그를 붙여주면 자바독 도구가 생성해준다.
기본값이 아니라 선택사항이라서 자바독 명령줄 매개변수로 지정해줘야함.
효율적인 하위 클래스를 만들려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다.
protected 메서드 하나하나가 내부 구현에 해당해서 그 수는 가능한 적어야 한다.
그렇다고 너무 적으면 상속으로 얻는 이점을 없애는것.
특별한 방법은 없으며 잘 예측하고, 실제 하위 클래스를 만들어 시험해봐야한다.
저자 경험상 3개면 적당하다 한다. 이러고 전혀 신경쓰이지 않는 멤버는 사실 private이었어야 할 가능성이 크다고한다.
상위 클래스의 생성자가 하위 클래스 생성자보다 먼저 실행.
즉, 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다.
public class Super {
// 잘못된 예 - 생성자가 재정의 가능 메서드를 호출한다.
public Super() {
overrideMe();
}
public void overriedMe() {
}
}
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(); // null, 2024-01...
}
}
상위 클래스의 생성자는 하위 클래스의 생성자가 인스턴트 필드를 초기화하기 전에 overrideMe를 호출해서 첫 번째에 null이 출력된다.
클래스 내부에서 자신의 메서드가 어떻게 사용되는지 모두 문서로 기록해야하고 그 내용은 클래스가 사용되는 곳에서 반드시 지켜야하는 등 상속용 클래스를 설계하기 쉽지 않다.
또한, 만약 지켜지지 않는다면 내부 구현방식을 믿고 활용하던 하위 클래스가 오동작할 수 있기 때문에
상속용으로 설계하지 않은 클래스는 상속을 금지하자.
클래스를 final로 선언하거나 생성자를 외부에서 접근하지 못하게 만들기.