67

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

那是什么?

据我所知,唯一剩下的区别(可能除了一些底层效率的东西)是抽象类遵循传统的 Java 单继承,而接口可以有多重继承(如果你愿意,也可以有多重实现)。这让我想到另一个问题——

新的 Java 8 接口如何避免菱形问题

4

5 回答 5

69

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

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

此外,不需要实现接口中的默认方法。所以通过这种方式,它不会破坏已经存在的代码,因为虽然接口确实收到了更新,但实现类不需要实现它。
因此,您可能会得到次优的代码,但如果您想要更优的代码,那么您的工作就是覆盖默认实现。

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

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

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 {

}

在这里,我得到编译器错误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();
    }
}

如果我想继承method()from B
同样适用于 ifD是 a class

为了更多地展示 Java 8 中接口和抽象类之间的区别,请考虑以下内容Team

interface Player {

}

interface Team {
    void addPlayer(Player player);
}

理论上,您可以提供一个默认实现,addPlayer以便您可以将玩家添加到例如玩家列表中。
可是等等...?
如何存储玩家列表?
答案是你不能在接口中这样做,即使你有可用的默认实现。

于 2014-03-23T13:28:32.127 回答
19

有一些非常详细的答案,但他们似乎遗漏了一点,我至少认为这是拥有抽象类的极少数理由之一

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

于 2014-03-28T19:29:33.053 回答
9

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

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

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

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

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

于 2014-03-23T13:27:41.620 回答
5

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

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

于 2014-03-23T13:35:29.077 回答
4

Java 8 虽然引入了接口的默认实现,这意味着这不再是接口和抽象类之间的关键区别。

仍然有一些更重要的差异。参考这篇文章:

Java 8 中默认方法与抽象类的接口

新的 Java 8 接口如何避免菱形问题?

案例1: 您正在实现两个具有相同default方法的接口,您必须解决实现类中的冲突

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}
interface interfaceB{
    default public void foo(){
        System.out.println("InterfaceB foo");
    }
}
public class DiamondExample implements interfaceA,interfaceB{
    public void foo(){
        interfaceA.super.foo();
    }
    public static void main(String args[]){
        new DiamondExample().foo();
    }
} 

上面的示例产生以下输出:

InterfaceA foo

案例 2: 您正在扩展一个基类并使用默认方法实现一个接口。编译器为您解决了菱形问题,您不必像第一个示例中那样解决它。

interface interfaceA{
    default public void foo(){
        System.out.println("InterfaceA foo");
    }
}

class DiamondBase {
    public void foo(){
        System.out.println("Diamond base foo");
    }
}

public class DiamondExample extends DiamondBase implements interfaceA{

    public static void main(String args[]){
        new DiamondExample().foo();
    }
}

上面的示例产生以下输出:

Diamond base foo
于 2016-11-20T02:45:59.910 回答