저번 포스트에서는 계승(상속)의 위험성을 설명하면서
구성의 디자인 패턴을 설명했습니다.
이번엔 EFJ에서는 상속의 위험성을 다루고 있습니다.
저번의 포스트에서 본 바와 같이 상속은 캡슐화를 깨뜨릴 수 있는 위험을 내포하고 있는 기능입니다.
때문에 오버라이드 등을 사용할 때 주의가 필요합니다.
1. 메소드를 재정의 할 때는 무슨 일이 발생하는지 정확하게 문서로 남겨야 한다.
/**
* {@inheritDoc}
*
* <p>This implementation iterates over the collection looking for the
* specified element. If it finds the element, it removes the element
* from the collection using the iterator's remove method.
*
* <p>Note that this implementation throws an
* <tt>UnsupportedOperationException</tt> if the iterator returned by this
* collection's iterator method does not implement the <tt>remove</tt>
* method and this collection contains the specified object. * * @throws UnsupportedOperationException {@inheritDoc}
* @throws ClassCastException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public boolean remove(Object o)
}
위는 java.util.AbstractCollection 의 remove() 메소드입니다.
이 내용을 읽어보면 remove가 호출 될 때 Iterator 가 사용된다는 것을 알 수 있으므로
하위 클래스에서 Iterator를 오버라이드 할 때 주의해야한다는 것을 알 수 있습니다.
물론 이런 상세만으로는 부족하고
클래스 내부 동작에 개입할 수 있는 훅(hooks)을 신중하게 고른 후, protected 메서드로 제공해야 합니다.
훅 메소드란 선택적으로 오버라이드 할 수 있는 메소드를 말합니다.
예를 들어
위와 같은 클래스가 있다고 생각해봅시다.
sub 클래스에서는 final 메소드는 오버라이드가 불가능하고, abstract는 반드시 오버라이드 해야 합니다.
일반 public는 오버라이드 여부를 상속하는 자가 정할 수 있죠.
이처럼 상속 여부를 정할 수 있는 메소드를 훅 메소드라 합니다.
또한 생성자에서 오버라이드가 가능한 메소드를 호출해서는 안됩니다.
public class Super {
public Super() {
overrideMe();
}
public void overrideMe() {
}
}
public class Sub extends Super {
private final Date date;
Sub() {
date = new Date();
}
@Overridepublic void overrideMe() { System.out.println(date); }
public static void main(String[] args) {
Sub sub = new Sub();
sub.overrideMe();
}}
상속을 하면 부모 클래스의 생성자가 자동적으로 호출되는 것을 알고 있으실 겁니다.
이 코드는 date가 두번 호출될 것으로 예상되지만
실제로는 첫호출 때 아무것도 호출되지 않습니다.
sub의 date가 호출되기 전에 상위 클래스의 생성자가 불려 overrideMe가 호출되기 때문이죠.
'프로그래밍 > Java' 카테고리의 다른 글
이펙티브 자바 규칙 18 - 추상 클래스 대신 인터페이스를 사용하자 (0) | 2018.02.17 |
---|---|
이펙티브 자바 규칙 16 - 계승하는 대신 구성하라 (0) | 2018.02.14 |
이펙티브 자바 규칙 15 - 변경 가능성을 최소화하라 (0) | 2018.02.12 |