首先明确:这里的继承只是指一个类拓展另一个类的情况,而不适用于接口继承(类实现接口,接口继承接口)。
继承的风险:1、继承打破了封装性(当超类发生变化时,自类可能遭到破坏,即使他的代码没有变化)。
2、下面的例子说明了继承中的另外一个问题。
// Broken - Inappropriate use of inheritance!
import java.util.*;
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;
}
public static void main(String[] args) {
InstrumentedHashSet<String> s =
new InstrumentedHashSet<String>();
s.addAll(Arrays.asList("Snap", "Crackle", "Pop"));
System.out.println(s.getAddCount());
}
}
客户端代码添加三个元素,语气输出结果是:3,但是实际输出结果是6.
这是因为HashSet内部,addAll()方法是基于add()方法来实现的。调用addAll()方法首先增加3,然后再依次调用三次add(),每次增加1,最终结果为6.
上述代码想要描述的继承导致的问题是:我们有的时候并不要知道父类方法的具体实现,在重写父类方法是就有可能发生错误。(导致这个问题的本质原因是“覆盖”,于是我们想要避免覆盖,比如:在子类中仅仅是添加新的方法,而不去重写。但是这样做还是存在隐患---在父类的后续版本中如果出现与该新方法签名(名字+参数列表)相同,但是返回类型不同时编译就会出错。