今天来看提示十八:复合优先于继承。
书中认为继承打破了封装,这是继承最大的危害,书中举了这样一个例子:
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;
}
}
这段代码看上去很正常,我也没有看出什么问题,但是在执行addAll方法的时候就会出现问题。原来在 HashSet 内部, addAll 方法是基于它的 add 方法来实现的,所以使用InstrumentedHashSet的addAll的时候会再次调用它自己的add方法多次,就会造成统计数据问题了。这一点我是真的没有想到,因为使用的super的方法,所以在不太清楚父类具体实现方法的时候盲目去使用继承是有很大风险的。导致子类脆弱的一个相关原因是,它们的父类在后续的发布版本中可以添加新的方法。
为了解决这两个缺点,书中提倡使用组合的方式来代替继承,使用has a而非is a可以解决很大一部分问题。继承功能更大强大,所以限制也更多,使用的时候值得我们更加警惕,真的符合is a的条件,再去使用。书中提到 Guava 为所有的 Collection 接口提供转发类,我特意去看了一下,发现果然Guava给这些类都提供了不可变类,以后使用的时候如果集合不会发生变化,可以考虑优先使用Guava的不可变版本。
可变集合接口 | 不可变版本 |
---|---|
Collection | ImmutableCollection |
List | ImmutableList |
Set | ImmutableSet |
SortedSet | ImmutableSortedSet |
Map | ImmutableMap |
SortedMap | ImmutableSortedMap |
网友评论