다른 패키지의 구체 클래스를 상속하는 것은 위험하다.
→ 메서드 호출과 달리 상속은 캡슐화를 깨트리기 때문!
상위 클래스에 새로운 메서드가 추가된다면 이 메서드를 사용해 허용되지 않은 동작을 수행할 수 있음
ex) 모든 메서드를 재정의해 필요한 조건 검사를 실행 후 수행하고 있는데 상위 클래스에 새로운 메서드가 추가된 경우 → 새로운 메서드는 조건 검사 없이 수행되는 일이 발생하게됨
하위 클래스에서 새로운 메서드만 추가한다면, 후에 상위 클래스에 시그니처가 같은 메서드가 추가될 경우 문제가 생길 수 있음 → 상위 클래스의 메서드 대신 하위 클래스에서 재정의한 메서드가 실행되어 잘못된 동작을 할 수 있음
기존 클래스를 확장하는 대신, 새로운 클래스를 만들고 private 필드로 기존 클래스의 인스턴스를 참조하는 방식
전달 (forwarding)
새 클래스의 인스턴스 메서드들은 기존 클래스의 대응하는 메서드를 호출해 그 결과를 반환
전달 메서드 (forwarding method)
새 클래스의 메서드들
이 방식을 사용하면, 새로운 클래스는 기존 클래스의 내부 구현 방식의 영향에서 벗어나게 됨
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s; // private 으로 Set 인스턴스를 참조
public ForwardingSet(Set<E> s) {
this.s = s;
}
@Override
...
}
import java.util.*;
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override
...
}
래퍼 클래스 사용 시 콜백 프레임워크와는 어울리지 않는다는 점만 주의하자.
래퍼 클래스와 SELF 문제
콜백 프레임워크에서는 자기 자신의 참조를 다른 객체에 넘겨서 다음 호출(콜백) 때 사용하도록 함
내부 객체는 자신을 감싸고 있는 래퍼의 존재를 모르니 대신 자신(this)의 참조를 넘기고, 콜백 때는 래퍼가 아닌 내부 객체를 호출하게 되는데, 이를 SELF 문제라고 함
→ 이로 인해 래퍼 클래스가 제대로 작동하지 않을 수 있음
전달 메서드가 성능에 주는 영향이나 래퍼 객체가 메모리 사용량에 주는 영향은 별다른 영향이 없다고 밝혀짐
클래스가 B가 클래스 A와 **is-a 관계(일반적인 개념과 구체적인 개념의 관계)**일때만 사용해야 함
클래스 A를 상속하는 클래스 B를 만드려고 한다면, “B가 정말 A인가?” 를 생각해봐야함
ex) 와인 클래스를 상속하는 레드 와인 클래스 (레드 와인은 와인이다.)
위 조건이 아니라면 A를 클래스 B의 private 인스턴스로 두고 사용하자