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(); } }
如果我想从B
inheritancemethod()
。
如果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 { }
钻石的具体问题是包容性和专 属性的问题。 如果你有一个B和C派生自A的types层次结构, D派生自B和C ,那么问题是:
- 是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