为什么Java或C#中不允许多重继承?

Abd*_*med 108 c# java language-design multiple-inheritance

我知道Java和C#中不允许多重继承.很多书只是说,不允许多重继承.但它可以通过使用接口来实现.没有讨论为什么不允许它.任何人都可以告诉我为什么不允许它?

Raz*_*zie 138

简短的回答是:因为语言设计者决定不这样做.

基本上,似乎.NET和Java设计者都不允许多重继承,因为他们认为添加MI会增加语言的复杂性,同时提供的好处太少.

为了更有趣和深入的阅读,网上有一些文章可以访问一些语言设计师.例如,对于.NET,Chris Brumme(曾在CLR的MS工作过)解释了他们决定不这样做的原因:

  1. 不同的语言实际上对MI的工作方式有不同的期望.例如,如何解决冲突以及重复的基数是合并还是冗余.在我们在CLR中实现MI之前,我们必须对所有语言进行调查,找出常见概念,并决定如何以语言中立的方式表达它们.我们还必须决定MI是否属于CLS,这对于不想要这个概念的语言(例如VB.NET)意味着什么.当然,这是我们作为公共语言运行时所处的业务,但我们还没有为MI做这件事.

  2. MI真正合适的地方实际上非常小.在许多情况下,多个接口继承可以完成工作.在其他情况下,您可以使用封装和委派.如果我们要添加一个稍微不同的构造,比如mixins,那实际上会更强大吗?

  3. 多实现继承为实现注入了很多复杂性.这种复杂性会影响投射,布局,调度,现场访问,序列化,身份比较,可验证性,反射,泛型以及许多其他地方.

你可以在这里阅读完整的文章.

对于Java,您可以阅读本文:

从Java语言中省略多重继承的原因主要来自"简单,面向对象,熟悉"的目标.作为一种简单的语言,Java的创建者需要一种大多数开发人员无需大量培训即可掌握的语言.为此,他们努力使语言尽可能与C++类似(熟悉),而不会带来C++不必要的复杂性(简单).

在设计师看来,多重继承会导致比解决更多的问题和混乱.因此,他们从语言中删除了多个继承(就像它们削减了运算符重载一样).设计师广泛的C++经验告诉他们,多重继承并不值得头疼.

  • 很好的比较.我认为,这两个平台都是思想过程的基础. (10认同)

duf*_*ymo 92

实现的多重继承是不允许的.

问题是,如果你有一个Cowboy和Artist类,编译器/运行时无法弄清楚该怎么做,两者都有draw()方法的实现,然后你尝试创建一个新的CowboyArtist类型.调用draw()方法时会发生什么?有人躺在街上死了,还是你有一个可爱的水彩画?

我相信它被称为双钻石继承问题.

  • 你可以在街上看到一个可爱的水彩画:-) (79认同)
  • 在C++中,您可以指定要派生的哪个基类函数,也可以自己重新实现它. (2认同)

use*_*551 19

原因: Java非常流行且易于编码,因为它简单.

因此,对于程序员来说,Java开发人员感到困难和复杂,他们试图避免它.一种这样的属性是多重继承.

  1. 他们避免使用指针
  2. 他们避免了多重继承.

多重继承问题:钻石问题.

示例:

  1. 假设A类有一个方法fun().B类和C类来自A类.
  2. B类和C类都覆盖了方法fun().
  3. 现在假设D类继承了B类和C类(只是假设)
  4. 为类D创建对象
  5. D d = new D();
  6. 并尝试访问d.fun(); =>它会调用B类的fun()或C类的乐趣()吗?

这是钻石问题中存在的模糊性.

解决这个问题并非不可能,但它在阅读时会给程序员带来更多的困惑和复杂性. 它导致的问题比它试图解决的问题更多.

注意:但是,任何方式都可以通过使用接口间接实现多重继承.


Dav*_*ley 13

因为Java与C++有着截然不同的设计理念.(我不打算在这里讨论C#.)

在设计C++时,Stroustrup希望包含有用的功能,无论它们如何被滥用.可以通过多重继承,运算符重载,模板和各种其他功能搞砸了大时间,但也可以用它们做一些非常好的事情.

Java设计理念是强调语言结构的安全性.结果是有些事情要做得更尴尬,但你可以更自信地看到你所看到的代码意味着你的想法.

此外,Java在很大程度上是来自C++和Smalltalk(最着名的OO语言)的反应.还有很多其他的OO语言(Common Lisp实际上是第一个被标准化的语言),具有不同的OO系统可以更好地处理MI.

更不用说使用接口,组合和委托在Java中完成MI是完全可能的.它比C++更明确,因此使用起来比较笨拙,但乍一看会让你更容易理解.

这里没有正确的答案.有不同的答案,哪一个更适合特定情况取决于应用程序和个人偏好.


Ste*_*eve 12

人们避开MI的主要原因(尽管不是唯一的)是所谓的"钻石问题",导致你的实施模糊不清.这篇维基百科文章对此进行了讨论,并对此进行了更好的解释.MI也可以导致更复杂的代码,许多OO设计师声称你不需要MI,如果你使用它,你的模型可能是错误的.我不确定我是否同意最后一点,但保持简单总是一个好计划.


ina*_*ruk 8

在C++中,如果使用不当,多重继承是一个令人头痛的问题.为了避免那些流行的设计问题,在现代语言(java,C#)中强制使用多个接口"继承".


mfx*_*mfx 8

多重继承是

  • 很难明白
  • 难以调试(例如,如果您将来自多个具有相同命名方法的框架的类混合在一起,可能会出现意想不到的协同效应)
  • 容易误用
  • 不是真的有用的
  • 难以实现,特别是如果你想要做正确有效

因此,将多重继承包含在Java语言中可以被认为是明智的选择.

  • 我在使用Common Lisp对象系统时不同意上述所有内容. (2认同)
  • 动态语言不算;-)在任何类似Lisp的系统中,你有一个REPL,使调试变得相当容易.此外,CLOS(据我所知,我从未真正使用它,只阅读它)是一个元对象系统,具有很大的灵活性和自己的态度.但是考虑一种静态编译的语言,比如C++,编译器使用多个(可能重叠的)vtable生成一些非常复杂的方法查找:在这样的实现中,找出调用方法的实现可能是一个不太重要的任务. (2认同)

小智 6

回到过去(70 年代),当时计算机科学更加科学,大规模生产较少,程序员有时间思考良好的设计和良好的实现,因此产品(程序)具有高质量(例如 TCP/IP 设计)和实施)。如今,当每个人都在编程,并且经理们在截止日期之前更改规范时,像 Steve Haigh 帖子中的维基百科链接中描述的那样的微妙问题很难跟踪;因此,“多重继承”受到编译器设计的限制。如果您喜欢,您仍然可以使用 C++ .... 并拥有您想要的所有自由:)

  • ...包括多次搬起石头砸自己脚的自由;) (7认同)

Bli*_*ndy 5

另一个原因是单继承使得转换变得微不足道,没有发出汇编指令(除了检查所需类型的兼容性之外).如果你有多重继承,你需要弄清楚某个父类在子类中的位置.所以表现肯定是一种振作(虽然不是唯一的).