Item 19. 상속을 고려해 설계하고 문서화하라. 그렇지 않았다면 상속을 금지하라

상속을 고려한 문서화

내부 구현 방법을 명시

  • 앞선 Item 18을 통해 상속을 이용할 시 하위 클래스에서 상위 클래스의 내부 구현 변경에 의해 의도하지 않은 부작용(side-effect)이 발생할 수 있음을 확인

  • 이를 예방하기 위해서는, 상속을 허용할 클래스라면 오버라이딩 가능한 메서드에 대해 내부 구현에 대한 문서를 남겨야 함

    • 자기사용(self-use) 여부 등을 명시하는 용도

  • 하지만 API 문서를 잘 쓰는 것은 해당 메서드가 어떻게 일을 하는지가 아니라 무엇을 하는지 설명하는 것이며, 이와 대치하는 방법

내부 구현에 포함된 메서드는 (내부에서만 이용함에도) protected로 선언해야 할 수 있음

  • 하위 클래스에서 상위 클래스의 구현 방법을 알고 있으므로, 내부 구현 메서드 자체를 오버라이딩하여 사용할 수도 있음

    • 하위 클래스에서 이를 잘 오버라이딩해 성능적 이슈를 얻을 수 있는 방법을 설명하기도 함

  • 또한, 하위 클래스에서 같은 동작을 하는 메서드를 선언하고자 한다면 이를 private으로 선언하면 무시됨

상속용 클래스를 시험하기 위해서는 하위 클래스를 직접 만들어 검증

  • 어떤 메서드를 protected로 만들지 등을 결정하기 위해서는 직접 3개 이상의 하위 클래스를 만들어 검증

  • 그 중 일부 클래스는 제3자가 만들게 하는 것이 좋음

상속용 클래스의 생성자는 절대 오버라이딩이 가능한 메서드를 호출하면 안 됨

  • 이는 생성자의 호출 시점과 연관이 있음

  • 이렇게 하면 상위 클래스 생성자 → 하위 클래스에서 오버라이딩한 메서드 → 하위 클래스 생성자 순으로 호출되는데, 이 때 하위 클래스 메서드에서 하위 클래스 생성자에서 초기화한 값을 사용한다면 정상적으로 동작하지 않게 됨

  • 이는 생성자만이 해당되는 규약이 아니라, 해당 객체를 생성하는 모든 메서드에 해당하는 규약

    • clone, readObject와 같은 메서드

    • 때문에 Cloneable, Serializable 인터페이스를 구현한 클래스를 상속하는 일은 위험성을 가짐

상속을 고려하지 않은 클래스는 상속을 금지

  • final 클래스로 선언하거나, 생성자를 private으로 선언한 뒤 대신 public 정적 팩토리를 제공하는 방식으로 상속을 금지

  • 그리고 외부에서는 컴포지션을 통해 이 클래스를 이용하도록 유도하기

Last updated