Java 8中的抽象类和接口有什么区别?

Dav*_*ica 64 java abstract-class interface diamond-problem java-8

在Java中,抽象类和接口之间曾经存在微妙但重要的区别:默认实现.抽象类可以拥有它们,接口却不能.Java 8虽然引入了接口的默认实现,但这意味着这不再是接口和抽象类之间的关键区别.

那是什么?

尽我所知,唯一剩下的差异(除了可能是引擎效率之外的东西)是抽象类遵循传统的Java单继承,而接口可以有多重继承(或者如果你愿意,可以有多个实现).这引出了另一个问题 -

新的Java 8接口如何避免钻石问题

ski*_*iwi 68

接口不能具有与之关联的状态.

抽象类可以具有与之关联的状态.

此外,不需要实现接口中的默认方法.因此,通过这种方式,它不会破坏已有的代码,因为当接口确实接收到更新时,实现类不需要实现它.
因此,您可能会获得次优代码,但如果您想拥有更优化的代码,那么您的工作就是覆盖默认实现.

最后,如果出现菱形问题,编译器会发出警告,需要选择要实现的接口.

要显示有关钻石问题的更多信息,请考虑以下代码:

interface A {
    void method();
}

interface B extends A {
    @Override
    default void method() {
        System.out.println("B");
    }
}

interface C extends A { 
    @Override
    default void method() {
        System.out.println("C");
    }
}

interface D extends B, C {

}
Run Code Online (Sandbox Code Playgroud)

在这里我得到编译器错误interface D extends B, C,即:

interface D inherits unrelated defaults for method() form types B and C

修复是:

interface D extends B, C {
    @Override
    default void method() {
        B.super.method();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果我想继承method()from B.
这同样适用于,如果D是一个class.

要更多地了解Java 8中接口和抽象类之间的区别,请考虑以下事项Team:

interface Player {

}

interface Team {
    void addPlayer(Player player);
}
Run Code Online (Sandbox Code Playgroud)

理论上,您可以提供默认实现,addPlayer以便您可以将玩家添加到例如玩家列表中.
可是等等...?
如何存储玩家列表?
答案是,即使您有可用的默认实现,也无法在界面中执行此操作.

  • 具有重写方法的示例并非特定于钻石问题.如果删除接口_A_,则无任何更改. (6认同)

Mar*_*o13 17

已经有一些非常详细的解答,但他们似乎丢失了一个点,我至少认为是极少数理由之一有抽象类:

抽象类可以具有受保护的成员(以及具有默认可见性的成员).接口中的方法是隐式公共的.

  • @ Dgrin91 是的。但是现在,在 Java 8 中,`default` 方法可以用在很多地方,否则人们可能会使用抽象类(正是这个方法是非抽象的)。以前,拥有抽象类的一个重要理由是实现通用的、可重用的方法。在许多(并非所有)情况下,现在可以使用“默认”方法解决此问题。所以我认为*可见性*方面变得更加重要。 (2认同)

nos*_*sid 8

钻石问题的定义含糊不清.多重继承可能会出现各种问题.幸运的是,大多数都可以在编译时轻松检测到,编程语言支持简单的解决方案来解决这些问题.大多数这些问题甚至不是钻石问题所特有的.例如,没有钻石也可能发生冲突的方法定义:

interface Bar {
    default int test() { return 42; }
}
interface Baz {
    default int test() { return 6 * 9; }
}
class Foo implements Bar, Baz { }
Run Code Online (Sandbox Code Playgroud)

钻石的具体问题是包容性排他性的问题.如果你有一个类型层次结构,其中BC派生自A,而D派生自BC,那么问题是:

  • D a B*和*a C(即A的一种类型),或
  • D a B*或*a C(即两种类型的A).

好吧,在Java 8中,类型A必须是一个接口.所以没有区别,因为接口没有状态.无关紧要,接口可以定义默认方法,因为它们也没有状态.他们可以调用直接访问状态的方法.但是,这些方法总是基于单继承实现.

  • 没有成员变量并不能解决钻石问题 - 如果没有使用任何内部状态,您仍然可以使用相同方法的两个冲突实现.通过简单地不允许类实现具有冲突的默认实现的两个接口来解决钻石问题. (9认同)

Phi*_*ipp 5

现在接口可以包含可执行代码,接口将接管许多用于抽象类的用例.但抽象类仍然可以有成员变量,而接口则不能.

当两个接口为具有相同签名的相同方法提供默认实现时,通过简单地不允许类实现两个接口来避免钻石问题.