为什么在Java或C#中不允许多重inheritance?
我知道在Java和C#中不允许多重inheritance。 很多书只是说,多重inheritance是不允许的。 但是可以通过使用接口来实现。 没有讨论为什么不被允许。 有谁能告诉我,为什么这是不允许的?
简短的回答是:因为语言devise者决定不这样做。
基本上,.NET和Javadevise者似乎都不允许多重inheritance,因为他们认为添加MI会给语言增加太多的复杂性 ,但却不能提供太多的好处 。
为了更加有趣和深入的阅读,网上有一些文章可以通过采访一些语言devise师。 例如,对于.NET来说,Chris Brumme(他曾在MS的CLR工作)解释了为什么他们决定不要:
不同的语言实际上对MI的工作方式有不同的期望。 例如,冲突如何解决,重复的基地是合并还是重复。 在CLR中实施MI之前,我们必须对所有语言进行调查,找出共同的概念,并决定如何以语言中立的方式来expression它们。 我们还需要决定MI是否属于CLS,这对于那些不想使用这个概念的语言(大概是VB.NET)意味着什么。 当然,这就是我们作为公共语言运行时所处理的业务,但是我们还没有做到这一点。
MI真正适合的地方其实是相当小的。 在很多情况下,多接口inheritance可以完成工作。 在其他情况下,您可以使用封装和委派。 如果我们添加一个稍微不同的构造,比如mixin,那会更强大吗?
多个实现inheritance为实现注入了很多复杂性。 这种复杂性会影响到投射,布局,调度,字段访问,序列化,身份比较,可validation性,reflection,generics以及其他许多地方。
你可以在这里阅读完整的文章。
对于Java,你可以阅读这篇文章 :
从Java语言中省略多重inheritance的原因主要来自“简单,面向对象和熟悉”的目标。 作为一种简单的语言,Java的创build者需要一种大多数开发人员无需广泛培训就能掌握的语言。 为此,他们努力使语言尽可能地类似于C ++(熟悉),而不必承担C ++不必要的复杂性(简单)。
在devise师看来,多重inheritance会导致更多的问题和困惑。 所以他们从语言中切断了多重inheritance(就像他们削减了操作符重载一样)。 devise师广泛的C ++经验告诉他们,多重inheritance是不值得头痛的。
实现的多重inheritance是不允许的。
问题在于如果你有一个Cowboy和一个Artist类,编译器/运行库都不知道该怎么做,同时也要执行draw()方法,然后尝试创build一个新的CowboyArtisttypes。 当你调用draw()方法时会发生什么? 有人在街上死了,还是有一个漂亮的水彩画?
我相信这就是所谓的双钻石遗传问题。
原因:由于 Java的简单性,Java非常stream行且易于编写代码。
所以,Java开发人员对程序员来说感到困难和复杂,他们试图避免它。 一种这样的属性是多重inheritance。
- 他们避免指针
- 他们避免了多重inheritance。
多重inheritance问题:钻石问题。
例如 :
- 假设类A有一个方法fun()。 B类和C类来自A类
- 而B和C类都重写fun()方法。
- 现在假设D类同时inheritance了B类和C类(正如假设)
- 为D类创build对象
- D d = new D();
- 并尝试访问d.fun(); =>它会调用类B的乐趣()或类C的乐趣()?
这是钻石问题中存在的模糊性。
解决这个问题并不是不可能的,但是在阅读的时候会给程序员造成更多的困惑和复杂性。 它导致更多的问题比它试图解决。
注意 :但是总是可以使用接口间接实现多重inheritance。
因为Java与C ++有着非常不同的devise理念。 (我不打算在这里讨论C#)
在deviseC ++时,Stroustrup希望包含有用的function,而不pipe它们如何被滥用。 有可能在多重inheritance,运算符重载,模板和其他各种function上搞砸了大部分时间,但是也可以用它们做一些很好的事情。
Java的devise理念是强调语言结构的安全性。 结果就是有些东西比较笨拙,但是你可以更加确信你所看到的代码意味着你的想法。
此外,Java在很大程度上是来自C ++和最知名的OO语言Smalltalk的反应。 还有很多其他OO语言(Common Lisp实际上是第一个被标准化的),具有不同的OO系统来更好地处理MI。
更不用说在Java中使用接口,组合和委派来完成MI是完全可能的。 它比C ++更明确,因此使用起来很笨拙,但会让你乍一看更容易理解。
这里没有正确的答案。 有不同的答案,哪一个更好的一个给定的情况取决于应用程序和个人喜好。
人们逃离MI的主要原因(虽然绝非唯一的原因)就是所谓的“钻石问题”,导致您的实施模糊不清。 这个维基百科文章讨论和解释比我更好。 MI还可能导致更复杂的代码,许多OOdevise者声称你不需要MI,如果你使用它,你的模型可能是错误的。 我不确定我是否同意这最后一点,但保持简单一直是一个很好的计划。
在C ++中,如果使用不当,多重inheritance是一个头痛的问题。 为了避免这些stream行的devise问题,现代语言(java,C#)强制使用多个接口“inheritance”。
另一个原因是单inheritance使得转换变得微不足道,没有发布汇编指令(除了检查需要的types的兼容性之外)。 如果你有多重inheritance,你需要弄清楚某个父项在子类中的起始位置。 所以,表演肯定是一种乐趣(尽pipe不是唯一的)。
多重inheritance是
- 很难明白
- 很难debugging(例如,如果混合使用同一名称方法的多个框架内部的类,则可能会出现意想不到的协同效应)
- 容易误用
- 不是真的有用
- 很难实现,特别是如果你想正确和高效地完成
因此,在Java语言中不包含多重inheritance是明智的select。
我用一点盐来说“Java中不允许多重inheritance”。
当“types”从多个“types”inheritance时定义了多inheritance。 接口也被分类为types,因为它们具有行为。 所以Java有多重inheritance。 只是这样更安全。
类的dynamic加载使多重inheritance的实现变得困难。
在Java中,他们避免了使用单一inheritance和接口的多重inheritance的复杂性。 在如下所述的情况下,多inheritance的复杂性非常高
钻石问题的多重inheritance。 我们有两个类B和Cinheritance自A.假设B和C重载了一个inheritance的方法,并且它们提供了它们自己的实现。 现在D从B和Cinheritance多重inheritance。 D应该inheritance那个重写的方法,jvm不能决定使用哪个重写的方法?
在c ++中使用虚函数来处理,我们必须明确地做。
这可以通过使用接口避免,没有方法体。 接口不能被实例化 – 它们只能由类实现或由其他接口扩展。
早在70年代,当计算机科学更加科学化,大规模生产时,程序员就有时间去思考良好的devise和良好的实施,因此产品(程序)具有高质量(如TCP / IPdevise和实施)。 如今,当每个人都在编程时,pipe理人员在最后期限之前改变规格,像史蒂夫·海格(Steve Haigh)这样的维基百科链接中描述的微妙问题很难跟踪。 因此,“多重inheritance”受编译器devise的限制。 如果你喜欢它,你仍然可以使用C ++ ….并拥有你想要的所有自由:)
Java有概念,即多态。 java中有两种多态性。 有方法重载和方法重载。 其中,方法覆盖发生在超级和子类关系上。 如果我们创build一个子类的对象并调用超类的方法,并且如果子类扩展了多个类,那么应该调用哪个超类方法?
或者,当通过super()
调用超类的构造函数时,哪个超类的构造函数会被调用?
目前的Java APIfunction无法做出这样的决定。 所以java中不允许多重inheritance。
实际上,如果inheritance的类具有相同的function,多重inheritance将会产生复杂性。 即编译器将会有一个必须select的混乱(钻石问题)。 所以在Java中,复杂性被移除了,并且赋予了接口来获得像多inheritance一样的function。 我们可以使用接口
Java中不允许直接使用多inheritance,但通过接口允许。
原因:
多重inheritance:引入更多的复杂性和模糊性。
接口:接口是Java中完全抽象的类,它为您提供了一个统一的方法,从公共可用的接口中正确地描述程序的结构或内部工作,其结果是具有更大的灵活性和可重用的代码以及更多的控制了解如何创build和与其他类进行交互。
更准确地说,它们是Java中的一个特殊的构造,具有额外的特性,允许您执行一种多重inheritance,即可以上传到多个类的类。
让我们以简单的例子。
-
假设有两个超类A和B具有相同的方法名称但function不同。 通过下面的代码(扩展)关键字多重inheritance是不可能的。
public class A { void display() { System.out.println("Hello 'A' "); } } public class B { void display() { System.out.println("Hello 'B' "); } } public class C extends A, B // which is not possible in java { public static void main(String args[]) { C object = new C(); object.display(); // Here there is confusion,which display() to call, method from A class or B class } }
-
但通过接口,(实现)关键字多重inheritance是可能的。
interface A { // display() } interface B { //display() } class C implements A,B { //main() C object = new C(); (A)object.display(); // call A's display (B)object.display(); //call B's display } }
有谁能告诉我,为什么这是不允许的?
你可以从这个文档链接find答案
Java编程语言不允许你扩展多个类的一个原因是为了避免多重inheritance状态的问题,这是inheritance多个类的字段的能力
如果允许多重inheritance,并且在通过实例化该类创build对象时,该对象将inheritance所有类的超类的字段。 这将导致两个问题。
-
如果来自不同超类的方法或构造函数实例化相同的字段呢?
-
哪个方法或构造函数优先?
即使现在允许多重inheritance状态,仍然可以实现
types的多重inheritance :一个类能够实现多个接口。
实现的多重inheritance (通过接口中的默认方法):能够从多个类inheritance方法定义
有关更多信息,请参阅此相关的SE问题:
带接口的多重inheritance歧义
想象一下这个例子:我有一个Shape1
类
它有CalcualteArea
方法:
Class Shape1 { public void CalculateArea() { // } }
还有另一个类Shape2
,也有一个相同的方法
Class Shape2 { public void CalculateArea() { } }
现在我有一个子类Circle,它来自Shape1和Shape2;
public class Circle: Shape1, Shape2 { }
现在,当我为Circle创build对象并调用方法时,系统不知道要调用哪个计算区域方法。 两者都有相同的签名。 所以编译器会混淆。 这就是为什么不允许多重inheritance。
但是可以有多个接口,因为接口没有方法定义。 即使两个接口有相同的方法,他们都没有任何实现,并且总是在子类中的方法将被执行。
- 转换List(of object)到List(of string)
- JSON的C#自动属性反序列化
- SMTP服务器需要安全的连接或者客户端没有被authentication。 服务器响应是:5.5.1需要身份validation?
- 如何将byte 转换为C#中的stream?
- inheritance一个通用的基类,应用一个约束,并在C#中实现一个接口
- C ++显示堆栈跟踪exception
- std :: unique_lock <std :: mutex>或std :: lock_guard <std :: mutex>?
- 最简单的方法在C#4.0中做一个火灾和遗忘的方法
- 应用程序无法加载。 Visual C#2015 RC编译器无法创build