상속은 직접 설계한 개발자 또한 문제를 일으킬 수 있는데, 협업하는 동료들은 어떠하겠는가?

그렇기 때문에 이번장에서는 상속 설계시 문서화에 대해 강조하고 있다.

 

API 문서의 메소드 설명 끝에서 종종 "Implement Requirements"로 시작하는 절을 볼 수 있는데,

그 메소드의 내부 동작 방식을 설명하는 곳이다.

 

이 절은 메소드 주석에 @implSpec 태그를 붙여주면 자바독 도구가 생성해준다.

그러나 @impleSpec이라는 정해진 태그가 있는 것이 아닌, 자바 개발팀 내부적으로 사용하는 규칙임을 이해해야 한다.

 

아무튼, 상속용 클래스를 설계할 때 어떠한 메소드를 protected로 노출할지 결정하기가 어렵다.

결국 하위 클래스를 만들고 계속해서 실험해보는 것이 정석적인 방법이다.

 

이 외에 상속을 허용하는 클래스가 지켜야 할 제약이 몇가지 더 있다.

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

 

예를 들면 다음과 같다

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

    public void overrideMe(){

    }
}

다음은 하위 클래스 코드다

public final class Sub extends Super(){
    //초기화 되지 않는 final 필드, 생성자에서 초기화 한다.
    private final Instant instant;

    Sub(){
        //Instant.now()를 하게되면 현재시간의 Instant객체를 얻을 수 있다
        instant = Instant.now();
    }

    @Override
    public void overrideMe(){
        System.out.println(instant);
    }

    public static void main(String[] args){
        Sub sub = new Sub();
        sub.overrideMe();
    }
}

객체 생성시에 overrideMe()가 실행되고, sub.overrideMe()로 총 두번 실행되니

사용자는 isntant가 두번 출력될 것이라 예상하지만

실상 첫번째는 null이 떨어진다.

 

그 이유는 하위 클래스가 초기화 되기전에 상위 클래스의 생성자가 overrideMe를 호출하기 때문이다.

여기에 앞에서 배웠던 Cloneable과 Serializable 인터페이스를 사용하게 되면 상속용 설계가 더욱 어려워진다.

 

결국 이런 문제를 해결하는 가장 좋은 방법은 상속용으로 설계하지 않은 클래스는 상속을 금지하는 것이다.

상속을 금지하는 방법은 클래스를 final로 선언하거나 생성자 모두를 외부에서 접근할 수 없도록 만들면 된다.

+ Recent posts