Java8之后何时使用接口与抽象类
我知道这个问题已经被问过很多次了,我们在互联网上有很多文章,但我仍然无法完全理解,因为我使用的是 Java 15,我应该什么时候使用接口或抽象类。
大多数文章都讨论了之前的差异和用例Java 8,但当您基本上可以在接口中为您的方法提供主体时,这些文章是有意义的。
唯一的那对我有意义的事情是non-public和non-final限制。
如果有人能指出我需要在 Java 15 中的接口和抽象类之间进行选择的场景的 1-2 个示例,我将非常感激。此外,如果它可以用于现实生活项目而不是动物或形状,那就太好了类示例。
谢谢 !!
回答
default 接口上的方法
显然,您指的是在接口中实现行为的“默认方法”功能。
您应该明白,添加该功能是为了解决这个难题:如何在不破坏实现这些接口的现有类的情况下,在现有接口上追溯添加利用流和 lambda 的功能?
许多新方法被添加到这些接口中,例如在 Java 集合框架中。向现有接口添加方法会自动破坏实现该接口的所有缺少新需要的方法的类。能够提供回退,在现在需要但尚不存在的情况下提供实现,将解决困境。因此,“默认方法”诞生了。
引用上面链接的 Oracle 教程:
默认方法使您能够向库的接口添加新功能,并确保与为这些接口的旧版本编写的代码的二进制兼容性。
引用此答案由作者Brian Goetz是Oracle的Java语言架构师:
为接口添加默认方法的最直接原因是支持接口演化
因此,向界面添加默认行为的这一特性本身并不意味着成为新的主流特性。的目的default不是取代abstract.
事实上,一些有经验的 Java 专家建议不要养成在接口上编写默认方法的习惯。他们建议几乎忽略 Java 语言的这一特性。继续将接口视为简单地定义契约,将抽象类视为提供旨在在子类中完成的部分实现。
您当然可以自由地在接口上编写自己的默认方法。当然,如果您处于发布了其他人可能已经实现的接口的类似情况,现在您想要添加方法,那么您应该这样做。但是不必要地添加默认方法可能会使其他希望在抽象类而不是接口上实现部分实现的程序员感到困惑。
default接口调用方法的四种情况
在上面链接的同一篇文章中,Brian Goetz 提出了接口演变之外的其他三种情况,其中接口上的默认方法可能是合适的。这是一个快速提及;有关详细信息,请参阅他的帖子。
- 可选方法 - 该
default方法抛出一个,UnsupportedOperationException因为我们期望这个接口的实现更经常不想实现这个方法。 - 方便的方法
- 组合器
从接口开始,转向共享代码的抽象类
至于在接口和抽象类之间进行选择:
- 一般从一个界面开始。如果您希望各种实现类混合各种合同,或者几个接口(请参阅mixin)。
- 在添加
default方法之前要三思。考虑您的情况是否符合上述 Brian Goetz 推荐的四种情况之一。
- 在添加
- 如果您意识到您在多个类中重复了代码,那么请考虑将共享代码集中到一个抽象类中,以便在子类之间使用。
- 或者,使用组合而不是继承(下面讨论)。
例如,你可能有一类为国内ShippingLabelUS以及ShippingLabelCanada和ShippingLabelOverseas。所有这三个都需要在英制磅和公制公斤之间转换。您发现自己在类之间复制了该代码。在这一点上,您可能会考虑让所有三个类都从抽象类扩展而来ShippingLabel,其中存在权重转换方法的单个副本。
在设计API 时,请记住,Java 与大多数OOP语言一样,具有单一继承的. 所以你的子类仅限于扩展一个类。为了更具体地了解单继承与多继承,我将从幻灯片的 PDF 中引用 Brian Goetz :
[关于
default接口上的方法]等等,这是Java中的多重继承吗?
• Java 总是有多重继承类型
• 这增加了行为的多重继承
• 但与州无关,大多数麻烦来自哪里
组合优于继承
对共享行为使用抽象类的另一种方法是为特定行为创建一个单独的类,然后添加该单独类的对象作为更大类的成员字段。聪明的程序员经常分享这颗智慧的珍珠:更喜欢组合而不是继承。
关于上面的运输标签示例,您可以创建一个WeightConverter类,该类的对象将是三个标签类中每一个的成员。在这种安排下,不需要抽象类。