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

Why is there no multiple inheritance in Java, but implementing multiple interfaces is allowed?

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


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

多重继承的问题在于,两个类可以定义不同的方法来做相同的事情,子类不能选择要选择哪一个。


我的一位大学老师这样对我解释:

Suppose I have one class, which is a Toaster, and another class, which is NuclearBomb. They both might have a"darkness" setting. They both have an on() method. (One has an off(), the other doesn't.) If I want to create a class that's a subclass of both of these...as you can see, this is a problem that could really blow up in my face here.

因此,一个主要的问题是,如果您有两个父类,它们可能具有相同特性的不同实现,或者可能具有相同名称的两个不同特性,就像在我的讲师的例子中那样。然后你必须决定你的子类将使用哪一个。当然,有很多方法来处理这个问题,当然C++也这样做了,但是Java设计者认为这会使事情变得过于复杂。

但是,对于接口,您描述的是类能够做的事情,而不是借用另一个类的方法做事情。与多个父类相比,多个接口更不可能导致需要解决的棘手冲突。


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

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


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

1
2
3
4
5
6
7
8
9
10
public class Bank {
  public void printBankBalance(){
    System.out.println("10k");
  }
}
class SBI {
 public void printBankBalance(){
    System.out.println("20k");
  }
}

编译后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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方法的新概念,请参考接口中的默认值。

希望这能解决您的问题。谢谢。


实现多个接口是非常有用的,不会给语言实现者和程序员带来太多的问题。所以这是允许的。多重继承虽然也很有用,但会给用户带来严重的问题(可怕的死亡钻石)。对于多重继承,您所做的大多数事情也可以通过组合或使用内部类来完成。因此,禁止多重继承,因为它带来的问题多于收益。


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

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

在1995年2月发表的一篇题为"Java:概述"的白皮书中,给出了一个关于Java中为什么不支持多重继承的概念。

根据戈斯林的说法:

"JAVA omits many rarely used, poorly understood, confusing features of
C++ that in our experience bring more grief than bene?t. This
primarily consists of operator overloading (although it does have
method overloading), multiple inheritance, and extensive automatic
coercions."


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

从C++ W/多元继承中吸取的教训是,它导致的问题多于它的价值。

接口是类必须实现的内容的契约。您无法从界面获得任何功能。继承允许您继承父类的功能(在多个继承中,这会变得非常混乱)。

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


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

  • 状态的多重继承:从多个类继承字段的能力


    One reason why the Java programming language does not permit you to extend more than one class is to avoid the issues of multiple inheritance of state, which is the ability to inherit fields from multiple classes

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

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

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

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

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

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

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

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


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

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


    因为这个主题还没有结束,我会发布这个答案,我希望这有助于人们理解为什么Java不允许多重继承。

    考虑以下类别:

    1
    2
    3
    4
    5
    6
    7
    public class Abc{

        public void doSomething(){

        }

    }

    在这种情况下,ABC类不扩展任何内容,对吗?不是很快,这个类隐式扩展了类对象,允许Java中的一切工作的基类。一切都是一个物体。

    如果您尝试使用上面的类,您会发现您的IDE允许您使用诸如:equals(Object o)toString()等方法,但是您没有声明这些方法,它们来自于基类Object

    你可以试试:

    1
    2
    3
    4
    5
    6
    7
    public class Abc extends String{

        public void doSomething(){

        }

    }

    这很好,因为您的类不会隐式地扩展Object,而是扩展String,因为您说过。考虑以下更改:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Abc{

        public void doSomething(){

        }

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

    }

    现在,如果调用ToString(),类将始终返回"hello"。

    现在想象一下下面的课程:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Flyer{

        public void makeFly(){

        }

    }

    public class Bird extends Abc, Flyer{

        public void doAnotherThing(){

        }

    }

    同样,类EDCOX1×5的隐式扩展对象具有EDCOX1方法1的方法,任何类都会有这种方法,因为它们都间接地扩展了EDCOX1×2,因此,如果从EDCOX1引用9调用EDCOX1×1,那么EDCOX1和1必须使用Java。来自AbcFlyer?任何试图扩展两个或更多类的类都会发生这种情况,为了避免这种"方法冲突",它们构建了接口的概念,基本上,您可以将它们视为不间接扩展对象的抽象类。因为它们是抽象的,所以必须由一个类来实现,该类是一个对象(不能单独声明接口,必须由一个类来实现),所以一切都将继续正常工作。

    为了使类与接口不同,只为接口保留了关键字实现。

    您可以在同一个类中实现您喜欢的任何接口,因为它们在默认情况下不扩展任何内容(但您可以创建扩展另一个接口的接口,但同样,"父"接口不会扩展对象),因此一个接口只是一个接口,如果它们执行e编译器将向您发出警告,您只需更改方法签名即可修复它(签名=方法名称+参数+返回类型)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    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

    }

    例如,两个类A、B具有相同的方法M1()。C类扩展了A,B。

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

    现在,C类将搜索M1的定义。首先,它将在课堂上搜索,如果没有找到,然后它将检查家长班。两个a,b都有定义,所以这里会出现歧义,应该选择哪个定义。所以Java不支持多重继承。


    因为接口只是一个契约。类实际上是数据的容器。


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


    由于歧义,Java不支持多重继承、多径和混合继承。问题:

    1
     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.

    多重继承

    Reference Link: https://plus.google.com/u/0/communities/102217496457095083679


    举个例子,A类有getsomething方法,B类有getsomething方法,C类扩展了A和B。如果有人调用C.getsomething会发生什么?无法确定要调用哪个方法。

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


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

    假设有三类:XYZ

    所以我们继承的就像江户十一〔三〕一样并且YZ都有一个方法alphabet()具有相同的返回类型和参数。Y中的方法alphabet()表示显示第一个字母,Z中的方法字母表示显示最后一个字母。因此,当alphabet()X调用时,就会出现歧义。是显示第一个字母还是显示最后一个字母????因此Java不支持多重继承。在接口的情况下,考虑使用YZ作为接口。因此,两者都将包含方法alphabet()的声明,而不是定义。它不知道是显示第一个字母表还是显示最后一个字母表,而是声明一个方法alphabet()。所以没有理由提出这种模糊性。我们可以在类X中用任何我们想要的东西定义方法。

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