클래스를 상속용으로 설계하려면 엄청난 노력이 들고 그 클래스에 안기는 제약도 상당하다.
일반적인 구체 클래스에서도 클래스에 변화가 생길 때마다 하위 클래스를 오동작하게 만들 수 있음
이 문제를 해결하는 가장 좋은 방법은 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것임
(클래스를 final로, 생성자를 private or package-private으로 선언 & public 정적 팩터리 만들기)
이 조언은 다소 논란의 여지가 있음. 그동안 수많은 프로그래머가 일반적인 구체 클래스를 상속해 계측, 통지, 동기화, 기능 제약 등을 추가해왔을테니 말이다.
1. 핵심 기능을 정의한 인터페이스가 있고 클래스가 그 인터페이스를 구현했다면 상속을 금지해도 개발하는 데 아무런 어려움이 없음(Set, List, Map이 좋은 예) — 래퍼 클래스 패턴 역시 더 나은 대안임
2. 구체 클래스가 표준 인터페이스를 구현하지 않았는데 상속을 금지하면 사용하기에 상당히 불편해짐. 이런 클래스를 상속 허용하기 위해서는 클래스 내부에서는 재정의 가능 메서드를 사용하지 않게 만들고 이 사실을 문서로 남기는 것임.(자기 사용 코드를 완벽히 제거해라)
제약사항
- 메서드를 재정의하면 어떤 일이 일어나는지를 정확히 정리하여 문서로 남겨야 함
- ↔ 상속용 클래스는 재정의할 수 있는 메서드(public과 protected중 final이 아닌 모든 메서드)들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다.
- 클래스의 API로 공개된 메서드에서 클래스 자신의 또 다른 메서드를 호출할 수 있다. 그런데 마침 호출되는 메서드가 재정의 가능 메서드라면 그 사실을 호출하는 메서드의 API 설명에 적시해야 함
- 어떤 순서로 호출하는지, 각각의 호출 결과가 이어지는 처리에 어떤 영향을 주는지도 담아야 한다.
- 넓게 말하면, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다.
- “좋은 API 문서란 ‘어떻게' 가 아닌 ‘무엇’을 하는지를 설명해야 한다” 라는 격언과는 대치되지만 클래스를 안전하게 상속할 수 있도록 하려면 내부 구현 방식을 설명해야만 함


- 내부 메커니즘을 문서로 남기는 것만이 상속을 위한 설계의 전부는 아니다. 효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다.
- clear 메서드를 고성능으로 만들기 쉽게 하기 위하여 removeRange를 재정의 가능하도록 공개
- 상속용 클래스를 설계할 때 어떤 메서드를 protected 로 노출해야 할지 어떻게 결정함??
- 마법은 없음.. 심사숙고해서 잘 예측해본 다음, 실제 하위 클래스를 만들어 시험해보는 것이 최선
- 시험하는 방법 → 직접 하위 클래스를 만들어보는 것이 ‘유일’
- 꼭 필요한 protected 멤버를 놓쳤다면 하위 클래스를 작성할 때 그 빈자리가 확연히 드러남
- 거꾸로, 하위 클래스를 여러 개 만들 때까지 전혀 쓰이지 않는 protected 멤버는 사실 private이었어야 할 가능성이 크다.
- 널리 쓰일 클래스를 상속용으로 설계한다면 여러분이 문서화한 내부 사용 패턴과, protected 메서드와 필드를 구현하면서 선택한 결정에 영원히 책임져야 함을 잘 인식해야함. ⇒ 배포 전에 반드시 하위 클래스를 만들어 검증

- 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안된다.
- 상위 클래스의 생성자가 하위 클래스의 생성자보다 먼저 실행되므로 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출됨
- Cloneable과 Serializable 인터페이스는 상속용 설계의 어려움을 한층 더해줌
- 그 클래스를 확장하려는 프로그래머에게 엄청난 부담을 지우기 때문
- clone 과 readObject 메서드는 생성자와 비슷한 효과를 냄(새로운 객체를 만듦) ⇒ clone과 readObject 모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안됨.
- clone 이 잘못되면 복제본 뿐 아니라 원본 객체에도 피해를 줄 수 있음. 복제본 내부 어딘가에서 여전히 원본 객체의 데이터를 참조하고 있는데 재정의 가능메서드가 값을 바꿔버리면 원본 객체도 영향 받음.
- Serializable 을 구현한 상속용 클래스가 readResolve나 writeReplace 메서드를 갖는다면 이 메서드들은 private 이 아닌 protected 로 선언해야 함. private으로 선언하면 하위 클래스에서 무시되기 때문에