Asa*_*nka 47 java inheritance interface abstract java-8
我不问这个 - > 为什么Java中没有多重继承,但允许实现多个接口?
在Java中,不允许多重继承,但是,在Java 8之后,Interfaces可以拥有默认方法(可以自己实现方法),就像抽象类一样.在此上下文中,还应允许多重继承.
interface TestInterface
{
// abstract method
public void square(int a);
// default method
default void show()
{
System.out.println("Default Method Executed");
}
}
Run Code Online (Sandbox Code Playgroud)
dav*_*xxx 36
事情并非如此简单.
如果一个类实现了多个定义具有相同签名的默认方法的接口,则编译器将强制您为该类重写此方法.
例如,使用这两个接口:
public interface Foo {
default void doThat() {
// ...
}
}
public interface Bar {
default void doThat() {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
它不会编译:
public class FooBar implements Foo, Bar{
}
Run Code Online (Sandbox Code Playgroud)
您应该定义/覆盖方法以消除歧义.
例如,您可以委托给Bar
实现,例如:
public class FooBar implements Foo, Bar{
@Override
public void doThat() {
Bar.super.doThat();
}
}
Run Code Online (Sandbox Code Playgroud)
或委托Foo
执行如下:
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
Foo.super.doThat();
}
}
Run Code Online (Sandbox Code Playgroud)
或者仍然定义另一种行为:
public class FooBar implements Foo, Bar {
@Override
public void doThat() {
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
该约束表明,即使对于接口默认方法,Java也不允许多重继承.
我认为我们不能对多重继承使用相同的逻辑,因为可能会出现多个问题,主要是:
int foo
在a A
和B
class中定义的字段你想要子类不具有相同的含义和意图.Eug*_*ene 25
语言设计者已经考虑过这一点,所以这些东西都是由编译器强制执行的.所以如果你定义:
interface First {
default void go() {
}
}
interface Second {
default void go() {
}
}
Run Code Online (Sandbox Code Playgroud)
并为两个接口实现一个类:
static class Impl implements First, Second {
}
Run Code Online (Sandbox Code Playgroud)
你会得到一个编译错误; 并且您需要覆盖go
以不在其周围创建歧义.
但你可能会认为你可以通过这样做来欺骗编译器:
interface First {
public default void go() {
}
}
static abstract class Second {
abstract void go();
}
static class Impl extends Second implements First {
}
Run Code Online (Sandbox Code Playgroud)
你可以认为First::go
已经提供了一个实现Second::go
,它应该没问题.这太过于谨慎,因此也无法编译.
JLS 9.4.1.3:类似地,当继承具有匹配签名的抽象和默认方法时,我们会产生错误.在这种情况下,可以优先考虑一个或另一个 - 也许我们假设默认方法也为抽象方法提供了合理的实现.但这是有风险的,因为除了巧合的名称和签名之外,我们没有理由相信默认方法与抽象方法的契约一致 - 默认方法在最初开发子接口时可能不存在.在这种情况下,要求用户主动声明默认实现是合适的(通过覆盖声明)更安全.
我要引入的最后一点是,即使在java中添加了新的东西,也不允许多重继承,这是接口的静态方法不会被继承.静态方法默认是继承的:
static class Bug {
static void printIt() {
System.out.println("Bug...");
}
}
static class Spectre extends Bug {
static void test() {
printIt(); // this will work just fine
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果我们为接口更改它(并且您可以实现多个接口,不像类):
interface Bug {
static void printIt() {
System.out.println("Bug...");
}
}
static class Spectre implements Bug {
static void test() {
printIt(); // this will not compile
}
}
Run Code Online (Sandbox Code Playgroud)
现在,编译器JLS
也禁止这样做:
JLS 8.4.8:类不从其超接口继承静态方法.
Pet*_*rey 13
Java不允许对字段进行多重继承.这在JVM中很难支持,因为您只能引用标题所在的对象的开头,而不是任意的内存位置.
在Oracle/Openjdk中,对象有一个标题,后跟最超类的字段,然后是下一个超级类的字段等.允许类的字段出现在相对于标题的不同偏移处是一个重大变化不同子类的对象.最有可能的对象引用必须成为对象头的引用和对字段的引用以支持它.
default
接口中的方法带来了一个问题:
如果两个实现的接口都定义了一个具有相同方法签名的默认方法,那么实现类不知道使用哪个默认方法。
实现类应该明确指定要使用的默认方法或定义它自己的方法。
因此default
,Java-8 中的方法不利于多重继承。默认方法背后的主要动机是,如果在某个时候我们需要向现有接口添加一个方法,我们可以在不更改现有实现类的情况下添加一个方法。这样,接口仍然兼容旧版本。但是,我们应该记住使用默认方法的动机,并应该保持接口和实现的分离。
多重继承的主要问题是排序(用于覆盖和调用super)、字段和构造函数;接口没有字段或构造函数,因此不会引起问题。
如果你看看其他语言,它们通常分为两大类:
具有多重继承的语言以及一些消除特殊情况歧义的功能:虚拟继承 [C++]、直接调用最底层派生类中的所有超级构造函数 [C++]、超类的线性化 [Python]、超级的复杂规则[ Python] 等。
具有不同概念的语言,通常称为接口、特征、混合、模块等,它们施加一些限制,例如:没有构造函数 [Java] 或没有带参数的构造函数 [Scala 直到最近]、没有可变字段 [Java]、特定的重写规则(例如 mixin 优先于基类 [Ruby],因此当您需要一堆实用方法时可以包含它们)等。Java 已经成为这样的语言。
为什么仅仅通过禁止字段和构造函数就可以解决与多重继承相关的许多问题?