메서드를 재정의하면 어떤 일이 일어나는지를 정확히 정리하여 문서로 남겨야 한다.
달리 말하면, 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다.
호출되는 메서드가 재정의 가능 메서드라면 그 사실을 호출하는 메서드의 API 설명에 적시해야 한다.
추가로 어떤 순서로 호출하는지, 각각의 호출 결과가 이어지는 처리에 어떤 영향을 주는지도 담아야 한다.
재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 하는 것이다.
좋은 API 문서란 '어떻게'가 아닌 '무엇'을 하는지를 설명해야 한다는 격언이 있다.
이와 대치된다 생각할 수 있다.
하지만 상속이 캡슐화를 해치기 때문에 일어나는 안타까운 현실이다.
클래스를 안전하게 상속할 수 있도록 하려면 내부 구현 방식을 설명해야만 한다.
내부 메커니즘을 문서로 남기는 것은 상속을 위한 설계의 전부는 아니다.
효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메서드 형태로 공개할 수도 있다.
그렇다면 상속용 클래스를 설계할 때 어떤 메서드를 protected로 노출해야 할지 결정할까?
상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어 보는 것이 유일하다.
널리 쓰일 클래스를 상속용으로 설계한다면 문서화한 내부 사용 패턴과, protected 메서드와 필드를 구현하면서 선택한 결정에 영원히 책임져야 함을 인식해야 한다.
그렇기에 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.
그리고 상속용 클래스가 지켜야 할 추가적인 제약이 있다.
상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
상위 클래스의 생성자가 하위 클래스의 생성자보다 먼저 실행되므로 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다.
이때 그 정의한 메서드가 하위 클래스의 생성자에서 초기화하는 값에 의존한다면 의도대로 동작하지 않을 것이다.
public class Super {
public Super() {
overrideMe(); // 재정의 가능 메서드가 호출된다. -> 의도대로 동작하지 않을 가능성이 큼
}
public void overrideMe() {
}
}
이렇게 보면 클래스를 상속용으로 설계하려면 엄청난 노력이 들고 그 클래스에 안기는 제약도 상당하다.
그렇기에 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이 좋다.
상속을 금지하는 방법은 클래스를 final로 선언하는 방법과 모든 생성자를 private이나 package-private으로 선언하고 public 정적 팩터리를 만들어 주는 방법이 있다.