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

상속용 클래스 정의시 주의사항

상속 가능한 클래스의 생성자에서 오버라이딩 가능한 메서드를 호출하는 일이 없도록 주의 해야한다. 만약 이 규약을 어기게 되면 예기치 못한 오류가 발생할 수 있다.

public class Super {
  public Super() {
    sample();
  }

  protected void sample() {
    //...doing
  }
}

아래는 해당 규약을 어긴 Super 클래스를 상속받아 구현한 클래스이다.
해당 클래스는 상위클래스의 sample 메소드를 오버라이드 하면서 멤버변수인 s를 별 생각없이 참조하도록 하고 있는데, 이것은 추후에 Child클래스가 인스턴스화 되는 시점에 에러를 유발하게 된다.

public class Child extends Super {
  private String s;

  public Child(String s) {
    this.s = s;
  }

  @Override
  public void sample(){
    System.out.println(s.toUpperCase()); 
  }
}

자, 이렇게 아래처럼 단지 Child 클래스를 초기화만 하였을 뿐인데 NullPointException이 발생하는 모습을 확인할 수 있다.

public MainClass {
  public static void main(String[] args) {
    new Child("s"); // NullPointException !!!
  }
}

왜 그럴까? 정답은 생성자의 호출 순서에 있다.
Child 클래스 초기화를 시도하면 Super 클래스의 기본 생성자가 먼저 실행된 후, Child 클래스의 생성자가 실행되게 된다. 결국 Child 클래스의 생성자가 호출되기 전에 Super 클래스의 생성자에서 멤버변수 s에 대한 참조를 시도하게 되어 오류가 발생하게 되는 것이다.