Skip to content

Latest commit

 

History

History
78 lines (51 loc) · 3.25 KB

File metadata and controls

78 lines (51 loc) · 3.25 KB

상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 - item19

💡 문서화? - 메서드를 재정의하면 어떤 일이 일어나는지 문서로 남겨라. - 상속용 클래스는 재정의할 수 있는 메서드들을 내부적으로 어떻게 이용하는지 문서로 남겨라.

좋은 API 문서는 어떻게가 아닌 무엇을 하는지를 설명해야 하지만, 상속이 캡슐화를 해치므로 내부 구현 방식을 설명해야한다.

API 문서의 메서드 설명 끝에 "Implementation Requirements”로 시작하는 절을 볼 수 있는데, 그 메서드의 내부 동작 방식을 설명하는 곳.

@implSpec 태그를 붙여주면 자바독 도구가 생성해준다.

기본값이 아니라 선택사항이라서 자바독 명령줄 매개변수로 지정해줘야함.

protected로 노출할 메서드

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

protected 메서드 하나하나가 내부 구현에 해당해서 그 수는 가능한 적어야 한다.

그렇다고 너무 적으면 상속으로 얻는 이점을 없애는것.

특별한 방법은 없으며 잘 예측하고, 실제 하위 클래스를 만들어 시험해봐야한다.

저자 경험상 3개면 적당하다 한다. 이러고 전혀 신경쓰이지 않는 멤버는 사실 private이었어야 할 가능성이 크다고한다.

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

상위 클래스의 생성자가 하위 클래스 생성자보다 먼저 실행.

즉, 하위 클래스에서 재정의한 메서드가 하위 클래스의 생성자보다 먼저 호출된다.

public class Super {
    // 잘못된 예 - 생성자가 재정의 가능 메서드를 호출한다.
    public Super() {
        overrideMe();
    }

    public void overriedMe() {

    }
}
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(); // null, 2024-01...
    }
}

상위 클래스의 생성자는 하위 클래스의 생성자가 인스턴트 필드를 초기화하기 전에 overrideMe를 호출해서 첫 번째에 null이 출력된다.

결론

클래스 내부에서 자신의 메서드가 어떻게 사용되는지 모두 문서로 기록해야하고 그 내용은 클래스가 사용되는 곳에서 반드시 지켜야하는 등 상속용 클래스를 설계하기 쉽지 않다.

또한, 만약 지켜지지 않는다면 내부 구현방식을 믿고 활용하던 하위 클래스가 오동작할 수 있기 때문에

상속용으로 설계하지 않은 클래스는 상속을 금지하자.

클래스를 final로 선언하거나 생성자를 외부에서 접근하지 못하게 만들기.