在Effective Java第2版第16项中,作者提供了一个场景,说明"继承中断封装"其中InstrumentedHashSet
继承自HashSet
并覆盖方法add()
和addAll()
。我的问题是为什么以下替代方案不能很好地解决我们的问题?可能会出现什么样的新问题:
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);
}
/* only the add method has been overrided
@Override public boolean addAll(Collection<? extends E> c) {
addCount += c.size();
return super.addAll(c);
}
*/
public int getAddCount() {
return addCount;
}
}
答案 0 :(得分:2)
如果您删除addAll
的覆盖,则InstrumentedHashSet
将容易受到addAll
中HashSet<T>
实施方式变化的影响。
目前,JDK implementation leaves the implementation up to its AbstractCollection
base class忠实地为每个项目调用add
:
public boolean addAll(Collection<? extends E> c) {
boolean modified = false;
Iterator<? extends E> e = c.iterator();
while (e.hasNext()) {
if (add(e.next())) // <<== This will call your override
modified = true;
}
return modified;
}
但是,可以采用另一种方法:例如,addAll
可以尝试通过在addAll
方法内一次扩展哈希桶数组来优化插入,而不是反复调用add
。这会破坏代码,因为addCount
将不再适当维护。
[来自评论]什么会促使Oracle中的Java语言设计者改变
HashSet<T>
实现?难道不会破坏许多使用addAll
调用在其实现中添加的预知知识设计的java程序吗?
这正是项目16试图说明的要点。一旦其他人开始从您的班级继承,您就会变得“锁定”在您的计划代码中未明确说明的实施方面,例如需要addAll
在每个项目上调用add
的方面。实际上,在没有明确确认的情况下,通过“冻结”代码的方面来继承破坏封装。