상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨야 한다.
재정의 가능 메서드
를 호출할 수 있는 모든 상황을 문서로 남겨야 한다.
💡 재정의 가능 메서드 : public과 protected 메서드 중 final이 아닌 모든 메서드.
-tag "implSpec:a:Implementation Requirents:"
를 지정해주면 된다.클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected
메서드 형태로 공개해야 할 수도 있다.
드물게는 protected 필드도 공개해야 할 수도 있다.
그렇다면 어떤 메서드를 protected로 노출해야 할지는 어떻게 결정할까?
안타깝게도 마법은 없다. 상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어보는 것이 유일하다.
저자의 경험상 이러한 검증은 하위 클래스 3개 정도를 만들어보는 것이 적당하다고 한다.
1️⃣ 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
private
, final
, static
메서드는 재정의가 불가능하니 생성자에서 안심하고 호출해도 된다.2️⃣ clone
과 readObject
모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.
Cloneable
과 Serializable
인터페이스 둘 중 하나라도 구현한 클래스를 상속할 수 있게 설계하는 것은 일반적으로 좋지 않은 생각이다. 이 인터페이스를 하위 클래스에서 구현하도록 하는 특별한 방법은 아이템 13과 86에서 설명한다.3️⃣ Serializable
을 구현한 상속용 클래스가 readResolve
나 writeReplace
메서드를 갖는다면 이 메서드들은 private이 아닌 protected
로 선언해야 한다.
일반적인 구체 클래서의 경우, 전통적으로 final도 아니고 상속용으로 설계되거나 문서화되지도 않았다.
이로 인해, 클래스에 변화가 생길 때마다 하위 클래스를 오동작하게 만들 수 있다.
이 문제를 해결하는 가장 좋은 방법은 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이다.
상속을 금지하는 방법은 2가지다.
1️⃣ 클래스를 final
로 선언한다. (더 쉬운 방법)
2️⃣ 모든 생성자를 private
이나 package-private
으로 선언하고, public
정적 팩터리를 만들어준다.
📌 핵심 정리
- 상속용 클래스는 내부에서 스스로를 어떻게 사용하는지 모두 문서로 남겨야 하며, 문서화한 것은 반드시 지켜야 한다.
- 다른 이가 효율 좋은 하위 클래스를 만들 수 있도록 일부 메서드를
protected
로 제공해야 할 수도 있다.- 클래스를 확장해야 할 명확한 이유가 없다면, 상속을 금지하는 것이 낫다.
- 상속을 금지하려면, 클래스를
final
로 선언하거나 생성자 모두를 외부에서 접근할 수 없도록 만들면 된다.