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

时间:2010-03-25 12:40:42

标签: java oop inheritance interface language-design

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

19 个答案:

答案 0 :(得分:202)

因为接口仅指定类正在做什么,而不是 它正在做什么。

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

答案 1 :(得分:89)

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

  

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

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

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

答案 2 :(得分:22)

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

public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter, 
             AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...

答案 3 :(得分:7)

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

答案 4 :(得分:7)

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

public class Bank {
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI {
 public void printBankBalance(){
    System.out.println("20k");
  }
}

编译后如下图所示:

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");
  }
}

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

以上代码可以正常运行。但是如果我们有另一个名为Car的类扩展了Bank和一个名为SBICar hybrid (多重继承)类:

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");
  }
}

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

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

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

希望这可以解决您的查询。 谢谢。

答案 5 :(得分:4)

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

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

1995年2月James Gosling撰写的题为“Java:an Overview”的白皮书(link)给出了为什么Java中不支持多继承的想法。

据Gosling说:

  

“JAVA省略了许多很少使用,知之甚少,令人困惑的特征   C ++,在我们的经验中带来更多的悲伤而不是有益。这个   主要由运算符重载组成(尽管它确实有   方法重载),多重继承和广泛的自动化   的强制“。

答案 6 :(得分:3)

出于同样的原因,C#不允许多重继承,但允许您实现多个接口。

从具有多重继承性的C ++中吸取的教训是,它导致的问题多于它的价值。

接口是您的类必须实现的事物的契约。您没有从界面获得任何功能。继承允许您继承父类的功能(并且在多重继承中,这可能会非常混乱)。

允许多个接口允许您使用设计模式(如适配器)来解决使用多重继承可以解决的相同类型的问题,但是以更加可靠和可预测的方式。

答案 7 :(得分:3)

您可以在oracle文档页面中找到有关此multiple inheritance

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

      

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

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

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

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

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

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

    What are the differences between abstract classes and interfaces in Java 8?

  3. 类型的多重继承: 类能够实现多个接口。

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

答案 8 :(得分:2)

由于这个主题不是很接近我会发布这个答案,我希望这有助于某人理解为什么java不允许多重继承。

考虑以下课程:

public class Abc{

    public void doSomething(){

    }

}

在这种情况下,Abc类没有任何扩展吗?不是那么快,这个类隐式扩展了类Object,它允许一切在java中工作。一切都是对象。

如果您尝试使用上面的类,您会看到您的IDE允许您使用以下方法:equals(Object o)toString()等,但您没有声明这些方法,它们来了来自基类Object

你可以尝试:

public class Abc extends String{

    public void doSomething(){

    }

}

这没关系,因为你的类不会隐式扩展Object,但会扩展String,因为你说了。请考虑以下更改:

public class Abc{

    public void doSomething(){

    }

    @Override
    public String toString(){
        return "hello";
    }

}

现在,如果调用toString(),您的类将始终返回“hello”。

现在想象下面的课程:

public class Flyer{

    public void makeFly(){

    }

}

public class Bird extends Abc, Flyer{

    public void doAnotherThing(){

    }

}

同样,类Flyer隐式扩展具有方法toString()的Object,任何类都将使用此方法,因为它们都间接扩展Object,因此,如果您调用toString()来自Bird,哪个toString() java必须使用?来自Abc还是Flyer?尝试扩展两个或更多类的任何类都会发生这种情况,为了避免这种“方法冲突”,他们构建了接口的概念,基本上你可以认为它们是抽象类这不会间接扩展Object 。由于它们是抽象,它们必须由一个类实现,这是一个对象(你不能单独实现一个接口,它们必须由一个类实现),所以一切都会继续正常。

为了使类与接口不同,关键字 implements 仅为接口保留。

你可以在同一个类中实现你喜欢的任何接口,因为默认情况下它们没有扩展任何东西(但你可以创建一个扩展另一个接口的接口,但同样,“父”接口不会扩展Object“),所以一个接口只是一个接口,它们不会受到“方法签名委托”的影响,如果他们这样做,编译器会向你发出警告,你只需要更改方法签名来修复它( signature =方法名+ params +返回类型)。

public interface Flyer{

    public void makeFly(); // <- method without implementation

}

public class Bird extends Abc implements Flyer{

    public void doAnotherThing(){

    }

    @Override
    public void makeFly(){ // <- implementation of Flyer interface

    }

    // Flyer does not have toString() method or any method from class Object, 
    // no method signature collision will happen here

}

答案 9 :(得分:2)

据说对象状态是相对于其中的字段引用的,如果继承了太多的类,它将变得模糊不清。这是链接

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

答案 10 :(得分:1)

例如,两个类A,B具有相同的方法m1()。 C类延伸A,B。

 class C extends A, B // for explaining purpose.

现在,C类将搜索m1的定义。首先,它将在课堂上搜索,如果它没有找到,那么它将检查父母班级。 A,B都有定义所以这里出现歧义,定义应该选择。 所以JAVA不支持多重遗产。

答案 11 :(得分:0)

因为界面只是一个合同。而一个类实际上是数据的容器。

答案 12 :(得分:0)

考虑一个场景,其中Test1,Test2和Test3是三个类。 Test3类继承Test2和Test1类。如果Test1和Test2类具有相同的方法并且您从子类对象中调用它,则调用Test1或Test2类的方法会有歧义,但是接口没有这样的歧义,因为在接口中没有实现。

答案 13 :(得分:0)

由于含糊不清,Java不支持多重继承,多路径和混合继承 问题:

 Scenario for multiple inheritance: Let us take class A , class B , class C. class A has alphabet(); method , class B has also alphabet(); method. Now class C extends A, B and we are creating object to the subclass i.e., class C , so  C ob = new C(); Then if you want call those methods ob.alphabet(); which class method takes ? is class A method or class B method ?  So in the JVM level ambiguity problem occurred. Thus Java does not support multiple inheritance.

multiple inheritance

  

参考链接: https://plus.google.com/u/0/communities/102217496457095083679

答案 14 :(得分:0)

例如,A类具有getSomething方法,B类具有getSomething方法,C类扩展A和B,如果有人调用C.getSomething,会发生什么情况?无法确定要调用的方法。

接口基本上只是指定实现类需要包含哪些方法。实现多个接口的类只意味着类必须实现所有这些接口的方法。 Whci不会导致如上所述的任何问题。

答案 15 :(得分:0)

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

  1. 在Java中,每个类都是Object类的子类。当它从多个父类继承时,子类就变得模棱两可 获取Object类的属性。
  2. 在Java中,每个类都有一个构造函数,无论我们是否明确编写它。第一条语句正在调用super()来调用 晚餐类构造函数。如果该类具有多个超级类,则它 感到困惑。

因此,当一个类从一个以上的超类扩展而来时,我们会得到编译时错误。

答案 16 :(得分:0)

我们都以简单的方式知道,我们可以继承(扩展)一个类,但是我们可以实现很多接口。.这是因为在接口中,我们没有给出实现只是说功能。假设Java可以扩展这么多的类并且那些类具有相同的方法..在这一点上,如果我们尝试在子类中调用超类方法,则假定要运行的是哪种方法?,编译器会感到困惑 example:- try to multiple extends 但是在接口中那些方法没有主体,我们应该在子类中实现它们。 try to multiple implements 所以不用担心。

答案 17 :(得分:-1)

*这是一个简单的答案,因为我是Java *的初学者

考虑有三个类XYZ

所以我们继承像X extends Y, Z YZ都有一个方法alphabet()具有相同的返回类型和参数。 alphabet()中的此方法Y表示显示第一个字母Z中的方法字母表示显示最后一个字母。 因此alphabet()调用X时会出现歧义。是否显示首先最后字母??? 所以java不支持多重继承。 如果是Interfaces,请将YZ视为接口。因此,两者都将包含方法alphabet()的声明,但不包含定义。它不会告诉你是否要显示第一个字母或最后一个字母或任何东西,只是会声明一个方法alphabet()。所以没有理由提出歧义。我们可以在类X中使用我们想要的任何内容来定义方法。

总而言之,接口定义是在实现后完成的,所以没有混淆。

答案 18 :(得分:-1)

保持低复杂度是一个决定。 使用混合继承,实现起来会更复杂,反正多重继承也可以用其他方式实现。