Answers:
因为接口只指定什么类是做,而不是如何它是做什么的。
多重继承的问题在于,两个类可能定义做同一件事的不同方式,而子类无法选择该选择哪一个。
我的一位大学讲师以这种方式向我解释了这一点:
假设我有一个班级,是一个烤面包机,另一个班级是,核弹。它们都可能具有“黑暗”设置。它们都具有on()方法。(一个有一个off(),另一个没有。)如果我想创建一个同时属于这两个子类的类……如您所见,这实际上是一个问题。 。
因此,主要问题之一是,如果您有两个父类,则它们可能具有相同功能的不同实现,或者可能有两个具有相同名称的不同功能,例如在我的讲师的示例中。然后,您必须确定子类将使用哪个子类。当然,有多种方法可以处理此问题(C ++可以这样做),但是Java的设计人员认为这会使事情变得过于复杂。
但是,通过一个接口,您正在描述类可以执行的操作,而不是借用另一个类的执行方法。与多个父类相比,多个接口引起需要解决的棘手冲突的可能性要小得多。
因为即使您不能说“嘿,该方法看起来很有用,继承也会被过度使用,所以我也将扩展该类”。
public class MyGodClass extends AppDomainObject, HttpServlet, MouseAdapter,
AbstractTableModel, AbstractListModel, AbstractList, AbstractMap, ...
这个问题的答案在于java编译器(构造函数链接)的内部工作。 如果我们看到Java编译器的内部工作原理:
public class Bank {
public void printBankBalance(){
System.out.println("10k");
}
}
class SBI extends Bank{
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
:
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)将无法创建构造函数链(编译时歧义)。
对于接口,这是允许的,因为我们无法创建它的对象。
对于新的概念default
和static
方法,请参考interface中的default。
希望这能解决您的查询。谢谢。
实现多个接口非常有用,并且不会对语言实现者或程序员造成太多问题。因此是允许的。多重继承虽然也很有用,但会给用户带来严重的问题(可怕的死亡钻石)。而且,您对多重继承所做的大多数事情也可以通过组合或使用内部类来完成。因此,禁止多重继承带来的问题多于收益。
ToyotaCar
和HybridCar
都派生自Car
和覆盖Car.Drive
,并且如果PriusCar
两者都继承但未被覆盖Drive
,则系统将无法识别虚拟机Car.Drive
应该做什么。接口通过避免上面的斜体字形条件来避免此问题。
void UseCar(Car &foo)
;不能期望包含ToyotaCar::Drive
和之间的歧义HybridCar::Drive
(因为通常不应该知道也不关心那些其他类型甚至存在)。像C ++一样,一种语言可能ToyotaCar &myCar
希望希望将其传递给的代码UseCar
必须首先强制转换为HybridCar
or或ToyotaCar
,但由于((Car)(HybridCar)myCar).Drive`并((Car)(ToyotaCar)myCar).Drive
会做不同的事情,这暗示了高调不保留身份。
您可以在oracle文档页面中找到有关多继承的此查询的准确答案。
状态的多重继承: 能够从多个类继承字段
Java编程语言不允许您扩展多个类的原因之一是避免状态的多重继承问题,即可以从多个类继承字段的能力
如果允许多重继承,并且在通过实例化该类创建对象时,该对象将继承该类所有超类的字段。这将导致两个问题。
实现的多重继承:能够从多个类继承方法定义
这种方法的问题:名称冲突和含糊不清。如果子类和超类包含相同的方法名称(和签名),则编译器无法确定要调用的版本。
但是java使用默认方法来支持这种类型的多重继承,这是自Java 8发布以来引入的。Java编译器提供了一些规则来确定特定类使用哪种默认方法。
有关解决钻石问题的更多详细信息,请参阅下面的SE帖子:
类型的多重继承: 类实现多个接口的能力。
由于接口不包含可变字段,因此您不必担心这里的状态多重继承引起的问题。
据说对象状态是相对于其中的字段引用的,如果继承了太多的类,它将变得模棱两可。链接在这里
http://docs.oracle.com/javase/tutorial/java/IandI/multipleinheritance.html
Java仅通过接口支持多重继承。一个类可以实现任意数量的接口,但只能扩展一个类。
不支持多重继承,因为它会导致致命的钻石问题。但是,它可以解决,但会导致系统复杂,因此Java创始人放弃了多重继承。
James Gosling在1995年2月发表的题为“ Java:概述”的白皮书中(链接)给出了一个为什么Java不支持多重继承的想法。
根据高斯林的说法:
“ JAVA遗漏了许多C ++很少使用,理解不清,令人困惑的功能,在我们的经验中,它们带来的痛苦大于好处。这主要包括运算符重载(尽管它确实有方法重载),多重继承和广泛的自动强制。”
出于相同的原因,C#不允许多重继承,但允许您实现多个接口。
从具有多重继承的C ++中学到的教训是,它导致了更多的问题,而不是值得的。
接口是您的类必须实现的事物的约定。您不会从该界面获得任何功能。继承使您可以继承父类的功能(在多重继承中,这可能会造成极大的混乱)。
允许多个接口使您可以使用设计模式(例如Adapter)来解决可以使用多重继承解决的相同类型的问题,但是要更加可靠和可预测。
D1
和D2
这两种继承B
,每个覆盖的功能f
,如果obj
是一个类型的实例S
,其来自继承D1
和D2
,但不会覆盖f
,然后铸造参考S
,以D1
应得的东西,其f
用途的D1
覆盖,并进行铸造而B
不能改变它。同样铸造了参考S
,以D2
应得到的东西,其f
用途的D2
覆盖,并铸造B
不应改变。如果一种语言不需要允许添加虚拟成员...
由于本主题尚未结束,因此我将发布此答案,希望这有助于某人理解为什么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
隐式扩展了Object,它具有方法toString()
,任何类都将具有此方法,因为它们都是Object
间接扩展的,那么,如果toString()
从中调用Bird
,则toString()
必须使用哪个java?来自Abc
还是Flyer
?任何尝试扩展两个或多个类的类都将发生这种情况,为了避免这种“方法冲突”,他们建立了接口,基本上您可以将它们视为不间接扩展Object的抽象类。由于它们是抽象的,因此必须由一个类(即一个对象)来实现 (您不能单独实例化一个接口,它们必须由一个类来实现),因此一切都将继续正常运行。
为了区别于接口的类,只为接口保留关键字Implements。
您可以在同一个类中实现您喜欢的任何接口,因为它们默认情况下不会扩展任何内容(但是您可以创建一个扩展另一个接口的接口,但是同样,“父”接口不会扩展Object”),所以一个接口是只是一个接口,它们将不会遭受“ 方法签名大肠菌 ”的困扰,如果这样做,编译器将向您发出警告,您只需要更改方法签名即可对其进行修复(签名=方法名称+参数+返回类型) 。
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
}
例如,两个具有相同方法m1()的A,B类。C类同时扩展了A,B。
class C extends A, B // for explaining purpose.
现在,类C将搜索m1的定义。首先,如果找不到,它将在班级搜索,然后它将检查到父母班级。A,B都具有定义,因此在这里出现歧义,应该选择哪个定义。因此,JAVA不支持多重继承。
Java不支持多重继承,原因有两个:
Object
级。当子类从多个超类继承时,子类将难以获得Object类的属性。super()
以调用晚饭类构造函数。如果该类具有多个超类,则会感到困惑。因此,当一个类从一个以上的超类扩展而来时,我们会得到编译时错误。
以这种情况为例:类A有一个getSomething方法,类B有一个getSomething方法,而类C扩展了A和B。如果有人叫C.getSomething,会发生什么?无法确定要调用的方法。
接口基本上只是指定实现类需要包含哪些方法。实现多个接口的类仅意味着该类必须实现所有这些接口中的方法。如上所述,不会导致任何问题。
考虑一个场景,其中Test1,Test2和Test3是三个类。Test3类继承了Test2和Test1类。如果Test1和Test2类具有相同的方法,并且您从子类对象调用它,则将有歧义地调用Test1或Test2类的方法,但是对于接口则没有歧义,因为在接口中没有实现。
由于不明确的问题,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.
参考链接: https : //plus.google.com/u/0/communities/102217496457095083679
我们都以简单的方式知道,我们可以继承(扩展)一个类,但是我们可以实现这么多的接口。这是因为在接口中,我们没有给出实现只是说功能。假设Java可以扩展这么多的类并且那些类具有相同的方法..在这一点上,如果我们尝试在子类中调用超类方法,该方法假设要运行?编译器会产生混淆的 示例:-尝试多次扩展, 但是在接口那些方法没有主体,我们应该在子类中实现它们。. 尝试使用多个实现, 因此没有后顾之忧。
*这是一个简单的答案,因为我是Java的初学者*
考虑到有三个类X
,Y
并且Z
。
因此,我们继承喜欢X extends Y, Z
而且两者Y
并Z
是有一个方法alphabet()
具有相同的返回类型和参数。该方法alphabet()
在Y
说显示第一个字母,而方法在Z
说显示最后一个字母。因此,当alphabet()
由调用时,这里会产生歧义X
。它说显示的是第一个字母还是最后一个字母???因此,java不支持多重继承。如果是接口,请考虑Y
和Z
视为接口。因此,两者都将包含方法的声明,alphabet()
而不包含定义。它不会告诉您显示第一个字母还是最后一个字母或其他任何内容,而只是声明一个方法alphabet()
。因此,没有理由提高歧义性。我们可以在类中使用任何我们想定义的方法X
。
总之,接口定义是在实现后完成的,因此不会造成混淆。