我不是在问-〉Why is there no multiple inheritance in Java, but implementing multiple interfaces is allowed?
在Java中,多重继承是不允许的,但是在Java8之后,接口可以有 * 默认方法 *(可以实现方法本身),就像抽象类一样。在这个上下文中,多重继承也应该是允许的。
interface TestInterface
{
// abstract method
public void square(int a);
// default method
default void show()
{
System.out.println("Default Method Executed");
}
}
7条答案
按热度按时间dfty9e191#
事情没那么简单。
如果一个类实现了多个接口,这些接口定义了具有相同签名的默认方法,则编译器将强制您重写该类的此方法。
例如,使用以下两个接口:
它不会编译:
您应该定义/重写该方法以消除歧义。
例如,您可以委托
Bar
实现,如:或委托给
Foo
实现,例如::或者仍然定义另一行为:
这个约束说明Java不允许多重继承,即使是接口的默认方法。
我认为,我们不能对多重继承适用同样的逻辑,因为可能会出现多重问题,主要有:
A
和B
类中定义的int foo
字段不具有相同的含义和意图。cedebl8k2#
语言设计者已经考虑过了,所以编译器会强制执行这些操作,如果你定义:
然后为两个接口实现一个类:
你会得到一个编译错误并且您需要覆盖
go
以避免在其周围产生歧义。但您可能认为可以通过以下操作来欺骗编译器:
你可能会认为
First::go
已经提供了Second::go
的实现,这应该没问题。这是太小心了,因此这也没有编译。JLS9.4.1.3:类似地,当一个抽象方法和一个具有匹配签名的默认方法被继承时,我们会产生一个错误。在这种情况下,可以给其中一个给予优先级--也许我们会假设默认方法也为抽象方法提供了一个合理的实现。但这是有风险的,因为除了名称和签名的巧合之外,我们没有理由相信默认方法的行为与抽象方法的契约一致--在子接口最初开发时,默认方法甚至可能还不存在.在这种情况下,让用户主动Assert默认实现是合适的(通过重写声明)会更安全。
最后一点我想强调的是,多重继承是不允许的,即使java中有新的添加,接口中的静态方法也是不被继承的。默认情况下,静态方法 * 是被继承的 *:
但是如果我们为一个接口改变它(并且你可以实现多个接口,不像类):
现在,编译器和
JLS
也禁止这样做:JLS 8.4.8:类不从其超接口继承静态方法。
1dkrff033#
Java不允许字段的多重继承,这在JVM中很难支持,因为你只能引用一个对象的头所在的开始,而不是任意的内存位置。
在Oracle/Openjdk中,对象有一个标头,后面跟着最大超类的字段,然后是下一个最大超类的字段,等等。允许一个类的字段相对于不同子类的对象标头出现在不同的偏移量处,这将是一个重大的变化。最有可能的是,对象引用必须成为对对象标头的引用和对字段的引用,以支持这一点。
oogrdqng4#
接口中的
default
方法会带来以下问题:如果两个实现的接口都定义了一个具有相同方法签名的默认方法,则实现类不知道使用哪个默认方法。
实现类应该显式地指定要使用的默认方法或定义自己的方法。
因此,Java-8中的
default
方法不便于多重继承。默认方法背后的主要动机是,如果在某个时候我们需要向现有接口添加一个方法,我们可以在不更改现有实现类的情况下添加一个方法。通过这种方式,接口仍然与旧版本兼容。然而,我们应该记住使用默认方法的动机,并且应该保持接口和实现的分离。yqlxgs2m5#
多重继承的主要问题是排序(用于重写和调用 super)、字段和构造函数;接口没有字段或构造函数,因此它们不会导致问题。
如果你看看其他语言,它们通常可以分为两大类:
1.具有多重继承的语言加上一些消除特殊情况歧义的特性:虚继承[C++]、直接调用最大派生类中的所有超构造函数[C++]、超类的线性化[Python]、super [Python]的复杂规则等。
1.具有不同概念的语言,通常称为 * 接口 、 特性 、 混合 、 模块 * 等,它们施加了一些限制,例如:没有构造函数[Java]或没有带参数的构造函数[Scala直到最近]、没有可变字段[Java]、特定的重写规则(例如,mixin优先于基类[Ruby],因此当您需要一堆实用方法时可以包含它们)等。Java已经成为这样的语言。
为什么仅仅通过禁用字段和构造函数就可以解决许多与多重继承相关的问题?
e5njpo686#
我认为这主要与“钻石问题”有关。现在如果你用同一个方法实现多个接口,编译器会强迫你重写你想实现的方法,因为它不知道使用哪个。我猜Java的创建者想在接口不能使用默认方法时消除这个问题。现在他们想出了一个主意,能够在接口中实现方法是很好的,因为你仍然可以在流/ lambda表达式中使用它们作为函数接口,并在处理中使用它们的默认方法。你不能在类中这样做,但钻石问题仍然存在。这是我的猜测:)
wgeznvg77#
Java确实支持多重继承。如果你对编程语言Java做一个全面的比较,那么你就会知道我是正确的。
Java的顶层类或祖先层次结构中的根类是Object类。该类是所有其他类的超类。因此,我们在Java中声明或在API中预定义的每个类都继承了这个Object类。
此外,Java还允许我们继承另一个类。
因此,我们可以说,我们正在执行联锁,但多重继承。
第二种方式
Java支持接口的多重继承。所以你可以使用任意多的接口实现。但是注意,实现一个接口并不定义IS A关系,因为类的继承是可能的。