계승
상속을 하게 되면 코드를 재활용하고 다형성을 사용할 수 있다는 점은 좋은 이점이지만 단점이 더 많다.
어떤 단점?
public class InstrumentedHashSet<E> extends HashSet<E> {
private int addCount = 0;
public InstrumentedHashSet() {}
public InstrumentedHashSet(int initCap, float loadFactor) {
super(initCap, loadFactor);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
다음과 같이 HashSet
InstrumentedHashSet<Integer> i = new InstrumentedHashSet<>();
i.addAll(Arrays.asList(1, 2, 3));
실행하고 결과를 보면 6이라는 결과 값이 나오게 된다. 왜 그러냐면 HashSet
그럼 어떻게?
새롭게 작성될 클래스에 기존 클래스를 private 멤버로 두는 것이다. 이런 설계 기법을 구성이라고 부르는데 기존 클래스가 새 클래스의 일부가 되기 때문이다.
// 계승 대신 구성하는 클래스
public class InstrumentedSet<E> extends ForwardingSet<E> {
private int addCount = 0;
public InstrumentedSet(Set<E> s) {
super(s);
}
@Override
public boolean add(E e) {
addCount++;
return super.add(e);
}
@Override
public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
public int getAddCount() {
return addCount;
}
}
// 재사용 가능한 전달 클래스
public class ForwardingSet<E> implements Set<E> {
private final Set<E> s;
public ForwardingSet(Set<E> s) {
this.s = s;
}
@Override
public int size() {
return s.size();
}
@Override
public boolean isEmpty() {
return s.isEmpty();
}
@Override
public boolean contains(Object o) {
return s.contains(o);
}
@Override
public Iterator<E> iterator() {
return s.iterator();
}
@Override
public Object[] toArray() {
return s.toArray();
}
@Override
public <T> T[] toArray(T[] a) {
return s.toArray(a);
}
@Override
public boolean add(E e) {
return s.add(e);
}
@Override
public boolean remove(Object o) {
return s.remove(o);
}
@Override
public boolean containsAll(Collection<?> c) {
return s.containsAll(c);
}
@Override
public boolean addAll(Collection<? extends E> c) {
return s.addAll(c);
}
@Override
public boolean retainAll(Collection<?> c) {
return s.retainAll(c);
}
@Override
public boolean removeAll(Collection<?> c) {
return s.removeAll(c);
}
@Override
public void clear() {
s.clear();
}
}
계승을 사용하게 되면 어떤 한 클래스에서만 적용이 가능하고, 상위 클래스 생성자 마다 별도의 생성자를 만들어야 한다. 하지만 포장 클래스 기법을 사용하면 어떠한 Set 클래스에 대해 구현할 수 있고 이미 있는 생성자도 그대로 사용할 수 있다.
그럼 상속은 언제?
옛날 부터 수 없이 들어 왔던 말이 있다 상속을 하기 위한 최선의 조건은 “IS-A”조건이 이다. 이 조건을 다시 한번 생각하면서 상속을 해야할지 포장을 해야할지 생각해야한다.