본문 바로가기

공부 기록/Java

[이펙티브 자바 3/E] 아이템19 - 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라

- 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지(자기사용) 문서로 남겨야 한다.

- 재정의 가능한 메서드 => public과 protected 메서드 중 final이 아닌 모든 메서드

- 더 넓게 말하면, 재정의 가능 메서드를 호출할 수 있는 모든 상황을 문서로 남겨야 한다. 예를 들어 백그라운드 스레드나 정적 초기화 과정에서도 호출이 일어날 수 있다.


- 내부 메커니즘을 문서로 남기는 것만이 상속을 위한 설계의 전부는 아니다.

- 효율적인 하위 클래스를 큰 어려움 없이 만들 수 있게 하려면 클래스의 내부 동작 과정 중간에 끼어들 수 있는 훅(hook)을 잘 선별하여 protected 메서드 형태로 공개해야 할 수도 있다. (드물게는 protected 필드로 공개해야 할 수도 있다.)

- 상속용 클래스를 시험하는 방법은 직접 하위 클래스를 만들어보는 것이 '유일'하다.

- 상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.


- 상속용 클래스의 생성자는 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.

 

- 잘못된 예시

public class Super {
    // 잘못된 예 - 생성자가 재정의 가능 메서드를 호출
    public Super() { overrideMe(); }
    
    public void overrideMe() { }
}
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();
    }
}

- 이 프로그램이 'instant'를 두 번 출력하리라 기대했겠지만, 첫 번째는 null을 출력한다. => 상위 클래스의 생성자는 하위 클래스의 생성자가 인스턴스 필드를 초기화하기도 전에 overrideMe를 호출하기 때문이다.


- 상속용 클래스에서 Cloneable이나 Serializable을 구현할지 정해야 한다면, 이들을 구현할 때 따르는 제약도 생성자와 비슷하다는 점에 주의하자. => clone과 readObject 메서드는 생성자와 비슷한 효과를 낸다(새로운 객체를 만든다).

- 즉, clone과 readObject 모두 직접적으로든 간접적으로든 재정의 가능 메서드를 호출해서는 안 된다.

 

- Serializable을 구현한 상속용 클래스가 readResolve나 writeReplace 메서드를 갖는다면 이 메서드들은 private이 아닌 protected로 선언해야 한다. => private으로 선언한다면 하위 클래스에서 무시되기 때문이다.


- 추상 클래스나 인터페이스의 골격 구현(아이템 20)처럼 상속을 허용하는 게 명백히 정당한 상황이 있고, 불변 클래스(아이템 17)처럼 명백히 잘못된 상황이 있다.

- 그렇다면 그 외의 일반적인 구체 클래스는 어떨까? => 상속용으로 설계하지 않은 클래스는 상속을 금지한다.

 

* 상속을 금지하는 방법

1. 클래스를 final로 선언한다.

2. 모든 생성자를 private이나 package-private으로 선언하고 public 정적 팩터리를 만들어준다.


[핵심 정리]

상속용 클래스를 설계하기란 결코 만만치 않다. 클래스 내부에서 스스로를 어떻게 사용하는지(자기사용 패턴) 모두 문서로 남겨야 하며, 일단 문서화한 것은 그 클래스가 쓰이는 한 반드시 지켜야 한다. 그러니 클래스를 확장해야 할 명확한 이유가 떠오르지 않으면 상속을 금지하는 편이 나을 것이다.