任何人都可以解释java中继承的缺点
答案 0 :(得分:9)
Effective Java™,第二版 作者:Joshua Bloch
第4章类和接口
第16项:赞成合成而不是继承
第17项:继承的设计和文件,否则禁止它
第18项:首选接口到抽象类
答案 1 :(得分:5)
看看Allen Holub在JavaWorld上发表的题为Why extends is evil的文章。他讨论了紧耦合问题和fragile base class问题。
答案 2 :(得分:3)
除非你的课程是为继承而设计的,否则它应该是最终的。您必须非常小心,以确保您了解将在子类中重写的方法,这些方法不是为继承函数设计的,以便了解如何修改它们。
具体示例:
考虑你有一个管理名单列表的类......
public class MyNameManager {
private List<String> numbers = new LinkedList<String>();
public void add(String value) {
numbers.add(value);
}
public void addAll(Collection<String> values) {
for(String value : values) {
add(value);
}
}
public void remove(String value) { //... }
//...
}
现在说你要创建一个新的子类,它也计算一个名称被添加到列表的总次数,如下所示:
public class MyCountingNameManager extends MyNameManager {
private int count = 0;
@Override
protected void addAll(Collection<String> values) {
count += values.size();
super.addAll(values);
}
@Override
protected void add(String value) {
count += 1;
super.add(value);
}
}
看起来很简单,不是吗?但请考虑以下结果:
MyCountingNameManager m = new MyCountingNameManager();
m.add("bob");
m.add("Sally");
计数现在是2,一切都很顺利。但是如果我们要做以下事情:
List<String> family = new List<String>();
family.add("mom");
family.add("dad");
family.add("brother");
MyCountingNameManager m = new MyCountingNameManager();
m.add(family);
现在计数为6,不您可能期望的3。这是因为对addAll
的调用会将值集合(大小为3)的大小添加到计数中,然后调用super.addAll
方法进行实际处理。 super.addAll
迭代Collection并为每个值调用add
方法。但由于我们使用MyCountingNameManager
和而非 a MyNameManager
,因此每次调用子类中重写的add
方法。执行的MyCountingNameManager.add
方法也会增加计数!结果是每个名字都被计算两次!
我相信这个例子来自Effective Java。你肯定应该找到一份副本并阅读Viele答案中列出的项目,以便更深入地了解继承不合适的一些情况。
答案 3 :(得分:2)
我们更喜欢组合而不是继承,因为当我们通过子类添加(特别)或更改功能时,我们将新功能耦合到类中 - 因此,只要我们需要新功能,我们就需要该类。这甚至扩展到了更多的子类 - 并且使用Java的单继承模型,如果我们在另一个类中有两个新的功能位,那么没有(简单)方法将两个位都带入,如果每个位都在一个单独的子类中原始的祖先。
相比之下,如果我们通过组合扩展功能,任何类 - 在我们现有的层次结构内外 - 都可以通过简单地包含具有新功能的小类来合并它。它更简单,更清洁,更可重复使用,更易于阅读。
继承有其地位,但它不是许多工作的正确工具。
答案 4 :(得分:0)
继承提供了类之间的紧密耦合关系,与运行时多态性的接口相比,它具有沉重的负担。