Effective Java 第三版——18. 组合优于继承

 Tips

《Effective Java, Third Edition》一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将近8年的时间,但随着Java 6,7,8,甚至9的发布,Java语言发生了深刻的变化。
在这里第一时间翻译成中文版。供大家学习分享之用。

Effective Java, Third Edition

18. 组合优于继承

继承是实现代码重用的有效方式,但并不总是最好的工具。使用不当,会导致脆弱的软件。 在包中使用继承是安全的,其中子类和父类的实现都在同一个程序员的控制之下。对应专门为了继承而设计的,并且有文档说明的类来说(条目 19),使用继承也是安全的。 然而,从普通的具体类跨越包级边界继承,是危险的。 提醒一下,本书使用“继承”一词来表示实现继承(当一个类继承另一个类时)。 在这个项目中讨论的问题不适用于接口继承(当类实现接口或当接口继承另一个接口时)。

与方法调用不同,继承打破了封装[Snyder86]。 换句话说,一个子类依赖于其父类的实现细节来保证其正确的功能。 父类的实现可能会从发布版本不断变化,如果是这样,子类可能会被破坏,即使它的代码没有任何改变。 因此,一个子类必须与其超类一起更新而变化,除非父类的作者为了继承的目的而专门设计它,并对应有文档的说明。

为了具体说明,假设有一个使用HashSet的程序。 为了调整程序的性能,需要查询HashSe,从创建它之后已经添加了多少个元素(不要和当前的元素数量混淆,当元素被删除时数量也会下降)。 为了提供这个功能,编写了一个HashSet变体,它保留了尝试元素插入的数量,并导出了这个插入数量的一个访问方法。 HashSet类包含两个添加元素的方法,分别是addaddAll,所以我们重写这两个方法:

// Broken - Inappropriate use of inheritance! public class InstrumentedHashSet<E> extends HashSet<E> {     // The number of attempted element insertions     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方法添加三个元素。 顺便提一句,请注意,下面代码使用在Java 9中添加的静态工厂方法List.of来创建一个列表;如果使用的是早期版本,请改为使用Arrays.asList

InstrumentedHashSet<String> s = new InstrumentedHashSet<>(); s.addAll(List.of("Snap", "Crackle", "Pop"));

我们期望getAddCount方法返回的结果是3,但实际上返回了6。哪里出来问题?在HashSet内部,addAll

50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信