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

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

86587 次浏览

人们避开 MI 的主要(尽管绝不是唯一)原因是所谓的“钻石问题”导致了实现中的模糊性。这个 维基百科文章讨论它和解释比我能更好。MI 还可能导致更复杂的代码,许多面向对象设计人员声称您不需要 MI,如果您确实使用了 MI,那么您的模型可能是错误的。我不确定我是否同意最后一点,但保持事情简单总是一个好计划。

多重继承是不允许的。

问题在于,如果您有一个 Cowboy 和一个 Artist 类,两个类都具有 draw ()方法的实现,然后您尝试创建一个新的 CowboyArtist 类型,那么编译器/运行时无法找到该做什么。当您调用 draw ()方法时会发生什么?是有人躺在街上死了,还是你有漂亮的水彩画?

我想这就是所谓的双钻石继承问题。

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

基本上,看起来。NET 和 Java 设计师不允许使用多重继承,因为他们认为,在提供 收益太少的同时,在语言中添加 MI 增加了太多的复杂性

为了更有趣和更深入的阅读,在网上有一些文章可以访问一些语言设计师。例如。克里斯 · 布鲁姆(Chris Brumme)(曾在微软公司的 CLR 项目工作)解释了他们决定不这么做的原因:

  1. Different languages actually have different expectations for how MI 工作。例如,如何冲突是 解决和是否重复的基础 合并或多余。在我们可以之前 在 CLR 中实现 MI,我们必须 所有语言、图形的调查 找出共同的概念,然后决定 如何表达它们 语言中立的态度。我们也会 必须决定 MI 是否属于 CLS 以及它的意义 不想要这个概念的语言 (例如,可能是 VB.NET) 当然,这就是我们的工作 作为一个通用语言运行库,但是我们 没时间为军情六处做这些 yet.

  2. 管理信息学真正适用的地方实际上相当多 小。在许多情况下,多个 interface inheritance can get the job 在其他情况下,你可 能够使用封装和 如果我们要增加一个 有点不同的结构 混音器,那会不会更多 强大

  3. 多个实现继承将大量复杂性注入到 这种复杂性 影响铸造,布局,调度, field access, serialization, identity 比较,可核实性, 反射,泛型,可能还有 很多其他地方

You can read the full article here.

对于 Java,可以读取 这篇文章:

省略多个 从 Java 语言继承 mostly stem from the "simple, object oriented, and familiar" goal. As a 简单的语言,Java 的创造者 想要一种大多数开发人员 不需要广泛的掌握 为此,他们努力工作 使该语言与 C + + 类似 可能的(熟悉的)没有携带 C + + 不必要的复杂性 (简单)。

在设计师看来,有很多 遗传会导致更多的问题 比解决困惑还要困惑,所以他们停止了 语言的多重继承 (正如他们切断操作员 overloading). The designers' extensive C + + 的经验告诉他们 多重继承不值得 头痛。

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

另一个原因是,单继承使得强制转换变得微不足道,不会发出汇编程序指令(除了在需要的地方检查类型的兼容性之外)。如果有多重继承,则需要确定某个父类从子类的哪个位置开始。因此,性能当然是一种福利(尽管不是唯一的福利)。

回到过去(70年代) ,当计算机科学更多的是科学而不是大规模生产的时候,程序员有时间去思考好的设计和好的实现,因此产品(程序)有高质量(例如:。TCP/IP 协议的设计和实现)。 如今,当每个人都在编程,管理者在截止日期前改变规格时,像维基百科链接中描述的 Steve Haigh post 那样的微妙问题很难跟踪,因此,“多重继承”受到编译器设计的限制。如果你喜欢它,你仍然可以使用 C + + ... 。拥有你想要的一切自由:)

I take the statement that "Multiple inheritance is not allowed in Java" with a pinch of salt.

当一个“类型”继承自多个“类型”时,多重继承就被定义为。接口也按其行为分类为类型。所以 Java 确实有多重继承。只是这样更安全。

多重继承是

  • hard to understand
  • 很难调试(例如,如果您混合来自多个框架的类,这些框架在内部具有相同名称的方法,就会发生意想不到的协同作用)
  • 很容易被误用
  • 那个没什么用
  • hard to implement, especially if you want it done correctly 还有 efficiently

因此,我们认为将 没有多重继承加入 Java 语言是明智的选择。

因为 Java 的设计理念与 C + + 大相径庭(我不打算在这里讨论 C #)

在设计 C + + 时,Stroustrup 希望包含有用的特性,而不管这些特性如何被滥用。多重继承、运算符重载、模板以及其他各种功能都可能搞砸,但也可能用它们做一些非常好的事情。

Java 的设计理念是强调语言构造的安全性。其结果是,有些事情做起来要笨拙得多,但是您可以更加自信地认为所看到的代码意味着您认为它所做的事情。

此外,Java 在很大程度上是 C + + 和 Smalltalk (最著名的面向对象语言)的反应。还有很多其他的面向对象语言(Common Lisp 实际上是第一个被标准化的语言) ,不同的面向对象系统能够更好地处理 MI。

更不用说在 Java 中完全可以使用接口、组合和委托来进行 MI。它比 C + + 更加明确,因此使用起来更加笨拙,但是会让你一眼就能看懂。

There is no right answer here. There are different answers, and which one is better for a given situation depends on applications and individual preference.

Java 有一个概念,即多态性。Java 中有两种类型的多态性。有方法重载和方法重载。其中,方法重写发生超类和子类关系。如果我们创建一个子类的对象并调用超类的方法,如果子类扩展了多个类,应该调用哪个超类方法?

或者,当通过 super()调用超类构造函数时,将调用哪个超类构造函数?

当前的 java API 特性无法做出这样的决定,因此 java 不允许多重继承。

类的动态加载使得多重继承的实现变得困难。

在 java 中,他们通过使用单一继承和接口避免了多重继承的复杂性。 在下面解释的情况下,多重继承的复杂性非常高

多重继承的钻石问题。 We have two classes B and C inheriting from A. Assume that B and C are overriding an inherited method and they provide their own implementation. Now D inherits from both B and C doing multiple inheritance. D should inherit that overridden method, jvm can't able to decide which overridden method will be used?

在 c + + 中,虚函数是用来处理的,我们必须显式地这样做。

这可以通过使用接口来避免,因为没有方法主体。接口不能实例化ーー它们只能由类实现或由其他接口扩展。

理由: Java 由于其简单性而非常受欢迎并且易于编写代码。

So what ever java developers feel difficult and complicated to understand for programmers, they tried to avoid it. One such kind of property is multiple inheritance.

  1. 他们回避指示
  2. 他们避开了多重继承。

多重继承问题: Diamond 问题。

Example:

  1. 假设类 A 有一个 fun ()方法。类 B 和类 C 派生自类 A。
  2. 类 B 和类 C 都覆盖方法 fun ()。
  3. 现在假设类 D 同时继承类 B 和类 C (只是假设)
  4. Create object for class D.
  5. 新 D () ;
  6. 然后尝试访问 d.fun () ; = > 它会调用 B 类的 fun ()还是 C 类的 fun () ?

This is the ambiguity existing in diamond problem.

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

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

Actually multiple inheritance will arise a the complexity if the inherited classes have same function. ie the compiler will have a confusion which one has to chose (diamond problem). So in Java that complexity removed and gave interface to get the functionality like multiple inheritance gave. We can use interface

在 Java 中不允许直接使用多重继承,但可以通过接口使用。

理由:

多重继承: 引入更多的复杂性和模糊性。

接口: 接口是 Java 中完全抽象的类,它提供了一种统一的方法来从公开可用的接口中正确地描述程序的结构或内部工作原理,其结果是更大的灵活性和可重用代码,以及对如何创建和与其他类交互的更多控制。

更确切地说,它们是 Java 中的一种特殊构造,具有额外的特性,允许您执行一种多重继承,即可以向上转换为多个类的类。

举个简单的例子。

  1. 假设有两个超类 A 和 B,它们具有相同的方法名,但是功能不同。通过下面的代码(扩展)关键字多重继承是不可能的。

       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
    }
    }
    
  2. But through interfaces, with (implements) keyword multiple inheritance is possible.

    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
    }
    }
    

想象一下这个例子: 我有一个类 Shape1

它采用 CalcualteArea方法:

Class Shape1
{


public void CalculateArea()


{
//
}
}

还有一个类 Shape2也有相同的方法

Class Shape2
{


public void CalculateArea()


{


}
}

现在我有一个子类 Circle,它同时派生自 Shape1和 Shape2;

public class Circle: Shape1, Shape2
{
}

现在,当我为 Circle 创建对象并调用这个方法时,系统不知道要调用哪个计算面积的方法。都有相同的签名。所以编译器会混淆。这就是为什么不允许多重继承的原因。

但是可以有多个接口,因为接口没有方法定义。即使这两个接口具有相同的方法,它们都没有任何实现,并且子类中的方法总是会被执行。

有人能告诉我为什么不允许吗?

You can find answer from this documentation 链接

Java 编程语言不允许扩展多个类的一个原因是为了避免状态多重继承的问题,即从多个类继承字段的能力

如果允许使用多重继承,并且当您通过实例化该类来创建对象时,该对象将继承该类的所有超类的字段。这会引起两个问题。

  1. 如果来自不同超类的方法或构造函数实例化同一个字段会怎样?

  2. 哪个方法或构造函数优先?

即使现在允许国家多重继承,你仍然可以实现

类型 的多重继承: 一个类实现多个接口的能力。

实现多重继承 (通过接口中的默认方法) : 从多个类继承方法定义的能力

请参考这个相关的 SE 问题以获得更多信息:

Multiple Inheritance Ambiguity with Interface

在 C + + 中,一个类可以(直接或间接)从多个类继承,这个类被称为 多重继承。

但是,C # 和 Java 将类限制为每个类继承的 单一继承权 从一个单一的父类。

多重继承是创建将两个不同类的方面结合在一起的类的有用方法 层次结构,这是在单个 application.

例如,如果两个框架为异常定义自己的基类,则可以 使用多重继承创建可以与任一框架一起使用的异常类。

多重继承的问题在于它可能导致歧义 一个类继承自另外两个类,每个类都继承自同一个类:

class A {
protected:
bool flag;
};
class B : public A {};
class C : public A {};
class D : public B, public C {
public:
void setFlag( bool nflag ){
flag = nflag; // ambiguous
}
};

在这个例子中,flag数据成员由 class A定义,但是 class Dclass B继承 和 class C,它们都来源于 A,所以本质上 flag两份是可用的,因为有两个 A的实例位于 D的类层次结构中。您想设置哪个? 编译器会报警 在 D中对 flag的引用是 模棱两可。一个修正是明确地消除引用的歧义:

B::flag = nflag;

另一个修复方法是将 B 和 C 声明为 virtual base classes,这意味着只有 A 的一个副本可以 存在于等级制度中,消除了任何歧义。

多重继承还存在其他复杂性,例如基类的顺序 当构造派生对象时初始化,或者成员可能无意中隐藏的方式 为了避免这些复杂性,一些语言限制自己使用更简单的单继承模型。

尽管这确实极大地简化了继承,但也限制了它的有用性 因为只有具有共同祖先的类才能共享行为 通过允许不同层次结构中的类甚至公开公共接口,在一定程度上限制了 如果它们不是通过共享代码实现的。