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

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

那么是什么?

尽我所知,唯一剩下的差别(除了可能是一些效率下面的东西),抽象类遵循传统的Java单一inheritance,而接口可以有多重inheritance(或多重实现,如果你愿意的话)。 这导致我另一个问题 –

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

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

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

而且,接口中的默认方法不需要实现。 所以这样就不会破坏已经存在的代码,因为当接口接收到更新时,实现类不需要实现它。
因此,您可能会得到不理想的代码,但是如果您希望获得更优化的代码,那么您的工作就是覆盖默认实现。

最后,如果发生钻石问题,编译器会警告你, 将需要select你想要实现的接口。

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

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(); } } 

如果我想从Binheritancemethod()
如果D是一个class

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

 interface Player { } interface Team { void addPlayer(Player player); } 

你可以在理论上提供addPlayer的默认实现,这样你可以添加玩家,例如玩家列表。
可是等等…?
我如何储存玩家名单?
答案是,即使您有可用的默认实现,您也无法在界面中执行此操作。

有一些非常详细的答案,但他们似乎缺less一点,我至less认为这是抽象类的几个理由之一:

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

钻石问题的定义是模糊的。 有多种inheritance可能发生的各种问题。 幸运的是,它们中的大多数可以在编译时轻松检测到,编程语言支持简单的解决scheme来解决这些问题。 这些问题大部分都不是钻石问题特有的。 例如,如果没有钻石 ,方法的定义也会相互冲突:

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

钻石的具体问题是包容性 属性的问题。 如果你有一个BC派生自A的types层次结构, D派生自BC ,那么问题是:

  • D a B *和* a C (即A的一种types),或者
  • D a B *或* a C (即A的两种types)。

那么,在Java 8中,typesA必须是一个接口 。 所以它没有区别,因为接口没有状态。 不要紧, 接口可以定义默认方法 ,因为它们也没有状态。 他们可以调用直接访问状态的方法。 但是,这些方法总是基于单一的inheritance来实现的。

既然接口可以包含可执行代码,那么很多抽象类的用例就被接口接pipe了。 但抽象类仍然可以有成员variables,而接口不能。

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

虽然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