아이템 19 - 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라 - 핵심 정리
이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다.
상속용 클래스는 내부 구현을 문서로 남겨야 한다.
상속 대신 컴포지션 을 사용하라고 했지만 상속은 객체지향의 가장 큰 특징이자
여러가지 코드를 재사용하는 좋은 방법 중 하나이기 때문에 어떻게하면 상속을 잘 사용할 수 있을지에 대한 아이템이다.
상속을 허용할거라면 문서화 를 해야한다.
상속용 클래스에스 재정의를 허용 하는 메서드에는 해당 메서드에 내부 동작 원리를 문서화해야한다.
좋은 API 는 어떻게가 아니라 무엇을 해야하는지에 대해 설명해야한다 라는 격언과 반대되는 말이다.
문서를 읽었을 떄 해당하는 클래스가 하는 역활을 파악할 수 있어야 좋은 문서이다.
내부 동작 원리를 구체적으로 설명하면 그 동작 원리에 묶이게 되므로 좋은 문서가 아니다.
상속을 허용하는 클래스에서 재공하는 재정의를 허용하는 메서드에서는 어떤식으로 동작하는지 구체적으로 문서화 를 해야한다.
해당 클래스를 상속해서 재정의할 때 구체적인 동작을 알아야하기 때문이다.
상속을 허용함으로써 캡슐화 가 꺠지기 때문에 내부 구현을 상세히 알아야한다.
/**
* Example class for java documentation for extendable class
*/
public class ExtendableClass {
/**
* This method can be overridden to print any message.
*
* @implSpec
* Please use System.out.println().
*/
protected void doSomething() {
System.out.println("hello");
}
}
재정의를 허용할 메서드에 @implSpec
태그를 이용해 문서를 작성한다.
@implSpec
태그는 자바8 부터 사용되었다.
@implSpec
태그에 동작 원리를 추가한다.
javadoc 을 생성할 때 -tag "implSpec:a:Implemetation Requirements:
옵션을 추가해야한다.
-tag <태그이름>:<위치>:<변경할 태그이름>
의 형식이다.
내부 동작 중간에 끼어들 수 있는 훅(hook)을 잘 선별하여 protected 메서드로 공개해야 한다.
재정의를 허용할 메서드를 잘 선별해야한다는 의미이다.
재정의할 메서드를 protected
로 만들어야한다.
상속용으로 설계한 클래스는 배포 전에 반드시 하위 클래스를 만들어 검증해야 한다.
상속을 가능한 클래스를 만들었으면 서브 클래스를 직접 만들어봐야한다.
최소한 3개를 만들것을 권장하며 그 중 한개는 본인이 아닌 다른 개발자를 통해 만들도록 권장한다.
필요한 대로 잘 만들어졌는지 검증하면서 protected
메서드를 조정한다.
상속용 클래스의 생성자는 재정의 가능한 메서드를 호출해서는 안 된다.
// 재정의 가능 메서드를 호출하는 생성자 - 따라 하지 말 것! (115쪽)
public class Super {
// 잘못된 예 - 생성자가 재정의 가능 메서드를 호출한다.
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
상속을 허용할 Super 클래스이다.
// 생성자에서 호출하는 메서드를 재정의했을 때의 문제를 보여준다. (126쪽)
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();
}
}
Super 클래스를 상속한 Sub 클래스를 작성한다.
상위 클래스의 생성자에서 재정의가 가능한 메서드를 호출하고 있다.
이렇게 생성자에서 재정의가 가능한 메서드를 호출하면 안된다.
상속을 받은 클래스는 상위 클래스의 생성자를 호출하게 된다.
Sub() {
super() // 생략 가능;
instant = Instant.now();
}
Sub 클래스를 생성할 때 상위 클래스인 Super 의 생성자를 호출하게 된다.
상위 클래스의 생성자에서 호출하는 메서드를 오버라이딩 했으므로
상위 클래스의 생성자에서 하위 클래스의 재정의한 메서드를 호출하게 된다.
같은 맥락으로 Cloneable 과 Serializable 을 구현할때는 조심해야한다.
Cloneable 과 Serializable 은 어떤 인스턴스를 만들어내는 특징이 있다.
Cloneable 은 현재 객체와 동일한 객체를 만들어내고 Serializable 은 역직렬화하며 바이트스트림을 객체로 복원하게된다.
생성자를 호출하는 것과 비슷한 효과가 있다.
Cloneable 과 Serializable 에서 메서드들에서 호출하는 메서드들을 재정의하면 안된다.
상속을 허용할 때 주의할 점과 문서화를 해야하는 일들이 많다.
상속을 허용할 것이라면 반듯이 위의 주의사항을 지켜주어야하고, 그렇지 않다면 상속을 금지 시켜야한다.
상속을 막는 방법은 final
클래스를 사용하는 것과
생성자를 모두 private
이나 package - private
으로 만드는 것이다.
생성자를 모두 package - private
으로 만들면 해당 패키지 내부에서 상속이 가능하다.
생성자를 모두 private
으로 만들면 해당 클래스 내부에서의 상속이 가능하다.
좀 더 클래스를 유연하게 사용할 것이라면 생성자를 모두 private
이나 package - private
만드는 방법도 좋은 방법이다.
'개발 공부 > Java' 카테고리의 다른 글
이펙티브 자바 아이템 20 - 추상 클래스보다 인터페이스를 우선하라 - 완벽 공략 (0) | 2022.11.29 |
---|---|
이펙티브 자바 아이템 20 - 추상 클래스보다 인터페이스를 우선하라 - 핵심 정리 (0) | 2022.11.28 |
이펙티브 자바 아이템 18 - 상속보다는 컴포지션을 사용하라 - 완벽 공략 (0) | 2022.11.24 |
이펙티브 자바 아이템 18 - 상속보다는 컴포지션을 사용하라 - 핵심 정리 (0) | 2022.11.22 |
이펙티브 자바 아이템 17 - 변경 가능성을 최소화 하라 - 완벽 공략 (0) | 2022.11.21 |
댓글