item18 · 复合优先于继承
此处继承不包括接口继承、包内部使用继承
继承打破了封装性
-
子类依赖于父类的实现,父类变化时子类会遭破坏
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 += 1; return super.add(e); } @Override public boolean addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); } public int getAddCount() { return addCount; } }
InstrumentedHashSet<String> s = new InstrumentedHashSet<>(); s.addAll(List.of("Snap", "Crackle", "Pop")); System.out.println(s.getAddCount()); // 6
对于这个结果的期望输出值是3,可实际上返回了6。
因为在
HashSet
内部,addAll
方法是基于它的add
方法来实现的,即使HashSet
的文档中没给出,但这也合理。 当然我们可以把重写的
addAll
方法中的加法去掉,但父类的实现对子类并不透明,在不同的发行版中可能会被修改,这样得到的InstrumentedHashSet
是非常脆弱的。 -
即使只添加新方法,如果父类在以后的发行版增加了新方法,而且签名和自定义的方法一样会造成严重的冲突
复合
-
通过
复合
(composition) 可以避免前面的所有问题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 += 1; 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(); } @Override public int hashCode() { return s.hashCode(); } @Override public boolean equals(Object obj) { return s.equals(obj); } @Override public String toString() { return s.toString(); } }
InstrumentedSet
是一个包装类,也是修饰者模式