为什么Java中没有多重继承,但允许实现多个接口?

abs*_*son 145 java oop inheritance language-design interface

Java不允许多重继承,但它允许实现多个接口.为什么?

Boz*_*zho 216

因为接口只指定什么类是做,而不是如何它是做什么的.

多继承的问题是两个类可能定义了做同样事情的不同方式,而子类无法选择要选择的方法.

  • 我曾经做过C++并且遇到过相同的问题很多次.我最近读到Scala有"特征",对我而言似乎是"C++"方式和"Java"方式之间的事情. (8认同)
  • 从Java 8开始,您可以定义两个相同的默认方法,每个接口一个.如果要在类中实现这两个接口,则必须在类本身中重写此方法,请参阅:http://docs.oracle.com/javase/tutorial/java/IandI/override.html#default (6认同)
  • 这个答案不准确.问题不在于指定_how_,Java 8就是要证明的.在Java 8中,两个_super interfaces_可以使用不同的实现声明相同的方法,但这不是问题,因为接口方法是_virtual_,因此您可以只覆盖它们并解决问题.真正的问题是关于**属性**中的**歧义**,因为你无法解决这种歧义覆盖属性(属性不是虚拟的). (5认同)
  • 这样,您就避免了"钻石问题":http://en.wikipedia.org/wiki/Diamond_problem#The_diamond_problem (2认同)

Syn*_*tic 90

我的一位大学教师用这种方式向我解释:

假设我有一个类,一个是Toaster,另一个类是NuclearBomb.它们都可能具有"黑暗"设置.它们都有一个on()方法.(一个有一个off(),另一个没有.)如果我想创建一个类,这是这两个的子类...正如你所看到的,这是一个问题,可能真的在我脸上爆炸了.

因此,主要问题之一是,如果您有两个父类,它们可能具有相同功能的不同实现 - 或者可能具有两个具有相同名称的不同功能,如我的教师示例中所示.然后你必须处理决定你的子类将使用哪一个.有一些方法可以解决这个问题 - 当然C++会这样做 - 但Java的设计者认为这会让事情变得太复杂.

但是,通过接口,您将描述类能够执行的操作,而不是借用另一个类的执行方法.与多个父类相比,多个接口不太可能导致需要解决的棘手冲突.

  • 如果有人决定混合使用核弹和烤面包机,他应该认为炸弹会在他的脸上爆炸.引用是_fallacious reasoning_ (18认同)
  • 谢谢,NomeN.引用的来源是一位名叫Brendan Burns的博士生,他当时也是开源Quake 2源代码库的维护者.去搞清楚. (5认同)
  • 这个类比的问题是,如果你要创建一个核弹和烤面包机的子类,"核多士炉"在使用时会合理地爆炸. (4认同)
  • 同一编程语言确实允许多个“接口”继承,因此这个“理由”不适用。 (2认同)

Mic*_*rdt 22

因为继承被过度使用,即使你不能说"嘿,那个方法看起来很有用,我也会扩展那个类".

public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter, 
             AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
Run Code Online (Sandbox Code Playgroud)

  • @DuncanCalvert:不,你不想那样做,如果那段代码需要维护的话.许多静态方法错过了OO的观点,但过多的多重继承会更糟糕,因为你完全忘记了在哪里使用哪些代码,以及类在概念上是什么.两者都试图解决"如何在需要的地方使用此代码"的问题,但这是一个简单的短期问题.适当的面向对象设计解决的更难的长期问题是"如何在不可预见的方式中在20个不同的地方打破程序,如何更改此代码? (7认同)
  • @DuncanCalvert:你通过拥有高内聚和低耦合的类来解决这个问题,这意味着它们包含彼此密集交互的数据和代码,但只能通过一个简单的小型公共API与程序的其余部分进行交互.然后,您可以根据API而不是内部细节来考虑它们,这很重要,因为人们只能同时记住有限的细节. (2认同)

Pra*_*ava 15

这个问题的答案在于Java编译器(构造函数链接)的内部工作。 如果我们看到Java编译器的内部工作原理:

public class Bank {
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI extends Bank{
 public void printBankBalance(){
    System.out.println("20k");
  }
}
Run Code Online (Sandbox Code Playgroud)

编译后看起来像这样:

public class Bank {
  public Bank(){
   super();
  }
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI extends Bank {
 SBI(){
   super();
 }
 public void printBankBalance(){
    System.out.println("20k");
  }
}
Run Code Online (Sandbox Code Playgroud)

当我们扩展类并创建其对象时,一个构造函数链将运行到Object类。

上面的代码可以正常运行。但是,如果我们有另一个Car扩展的类Bank和一个混合(多重继承)类,则SBICar

class Car extends Bank {
  Car() {
    super();
  }
  public void run(){
    System.out.println("99Km/h");
  }
}
class SBICar extends Bank, Car {
  SBICar() {
    super(); //NOTE: compile time ambiguity.
  }
  public void run() {
    System.out.println("99Km/h");
  }
  public void printBankBalance(){
    System.out.println("20k");
  }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,(SBICar)将无法创建构造函数链(编译时歧义)。

对于接口,这是允许的,因为我们无法创建它的对象。

对于新的概念defaultstatic方法,请参考interface中的default

希望这能解决您的查询。谢谢。

  • 这个答案已经回到前面了。编译器和解释器被编程来实现语言设计决策,而不是相反。 (2认同)

Rav*_*abu 8

您可以在有关多重继承的oracle 文档页面中找到此查询的准确答案

  1. 状态的多重继承: 能够从多个类继承字段

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

    如果允许多重继承并且当您通过实例化该类来创建对象时,该对象将从该类的所有超类中继承字段。它会导致两个问题。

    1. 如果来自不同超类的方法或构造函数实例化同一个字段怎么办?
    2. 哪个方法或构造函数优先?
  2. 实现的多重继承:能够从多个类继承方法定义

    这种方法的问题:名称冲突歧义。如果子类和超类包含相同的方法名称(和签名),编译器无法确定要调用哪个版本。

    但是java支持这种带有默认方法的多重继承,这是自 Java 8 发布以来引入的。Java 编译器提供了一些规则来确定特定类使用的默认方法。

    有关解决钻石问题的更多详细信息,请参阅以下 SE 帖子:

    Java 8 中的抽象类和接口之间有什么区别?

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

    由于接口不包含可变字段,因此您不必担心此处状态的多重继承导致的问题。


Tad*_*pec 7

实现多个接口非常有用,并且不会给语言实现者和程序员带来太多问题.所以这是允许的.多重继承虽然也很有用,但可能会给用户带来严重的问题(可怕的死亡钻石).您使用多重继承执行的大多数操作也可以通过组合或使用内部类来完成.因此,禁止多重继承带来更多问题而不是收益.

  • @curiousguy包含多个相同基类的子对象,歧义(覆盖基类的使用),解决这种歧义的复杂规则. (2认同)

小智 7

Java不支持多重继承有两个原因:

  1. 在java中,每个类都是Object类的子类。当子类继承了多个超类时,子类就获得了Object类的属性的歧义性。
  2. 在java中,每个类都有一个构造函数,无论我们显式地编写它还是根本不编写它。第一条语句是调用super()超级类构造函数。如果一个类有多个超类,它就会变得混乱。

因此,当一个类从多个超类扩展时,我们会得到编译时错误。


Pra*_*mar 6

Java 仅支持通过接口进行多重继承。一个类可以实现任意数量的接口,但只能扩展一个类。

不支持多重继承,因为它会导致致命的菱形问题。然而,它是可以解决的,但它会导致系统复杂,因此 Java 创始人放弃了多重继承。

在 James Gosling 于 1995 年 2 月(链接 - 第 2 页)题为“Java:概述”的白皮书中,给出了 Java 不支持多重继承的想法。

根据高斯林的说法:

“JAVA 忽略了 C++ 中许多很少使用、理解不足、令人困惑的特性,根据我们的经验,这些特性带来的弊大于利。这主要包括运算符重载(尽管它确实有方法重载)、多重继承和广泛的自动强制转换。”


小智 5

据说对象状态是根据其中的字段来引用的,如果继承了太多的类,它就会变得不明确。链接在这里

http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html