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

Roa*_*oam 6 inheritance abstract-class interface java-8

我想找到一个完整的答案:

" 为什么/何时使用抽象类而不是接口."

并寻找以下的验证/建议.

对此的答案是,

"为具体类型提供实现.在具体类进入以定义特定类型之前,一个抽象类,通常位于继承层次结构中的接口下方(如Java API中的许多示例)实现和引脚关于接口定义的结构的一些常见方面.

使用抽象类的另一个好理由是类型之间存在明确的逻辑层次结构.抽象类可以用来组织该层次结构,同时通过一个抽象类而不是一个具体的类来强制对象的实例化只在下面这个层次结构中的具体类中完全定义,因此是有意义的.

然后,在这种情况下,来Q:

"从Java 8开始,接口可以定义默认方法实现.为什么我不在接口中编写默认方法而不是实现这些方法的抽象类?"

答案是:

"一个原因是:接口方法都是公共的,字段成员都是常量(最终和公共).您可能希望限制方法的访问权限和/或使它们在非常量状态下操作.

另一个是:后代类可以通过super调用抽象类方法,而在默认接口方法上则不能这样做.此外,接口没有后代调用的构造函数.

其余原因与上面的Java 8之前的原因相同."

除了以上这些,为什么我宁愿选择一个抽象类而不是一个接口,特别是现在我拥有自Java 8以来的默认接口方法?

请注意:我见过的Java 8默认的方法与在抽象类中的非抽象方法,并用默认的方法VS Java中8抽象类接口与其他一些有益的讨论一起.这个Q更倾向于为什么在界面上选择抽象类而不是相反.

TIA.

ass*_*ias 7

具有默认方法的接口只能定义行为,而抽象类可以具有状态.

换句话说,如果有一个需要在子类层次结构中共享的成员变量,则应该使用抽象类(这里的共享意味着如果子类不是私有的或通过方法,则子类可以直接访问它).

抽象类的另一个用例是,如果要覆盖某些Object方法,例如equalstoString.这不能通过默认方法完成.


en_*_*ght 7

以下是在界面上选择抽象类的一些原因.你列出的那些 - 私人领域等,是一些主要的领域.这些是一些更微妙的

  • 类型清晰度.你只能扩展一个类.这样可以更清楚地了解您的对象是如何使用它的.
  • 钻石问题.您必须在接口中实现所有默认方法,以帮助避免钻石问题.如果接口是Collections,这可能是一个巨大的痛苦,因为它们有十亿.使用抽象类,您只需覆盖您需要的内容
  • 在Java 8中,存在lambda表达式,并且已经篡改了使用要实现的方法传递接口的旧例程.但是,当您看到具有默认方法的接口时,可以按照您可能不想要的方式进行解释

以下是Oracle就此主题所说的话:

你应该使用哪些,抽象类或接口?如果任何这些语句适用于您的情况,请考虑使用抽象类:

  • 您希望在几个密切相关的类之间共享代码.
  • 您希望扩展抽象类的类具有许多常用方法或字段,或者需要除公共之外的访问修饰符(例如protected和private).
  • 您想声明非静态或非最终字段.这使您可以定义可以访问和修改它们所属对象的状态的方法.

在本文中,Orace捍卫了两种类型系统之间的区别 https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html

编辑澄清"钻石问题"子弹这里描述的问题(以及许多其他地方) http://www.lambdafaq.org/what-about-the-diamond-problem/

从两个声明相同方法的地方继承时会出现问题,并且在解析函数调用时必须选择一个.这在Java 7中从来都不是问题,因为你只能扩展一个类而接口没有方法.

解决策略现在有点复杂,但这里有一个很好的描述:(来自http://blog.loxal.net/2013/05/java-8-default-interface.html)

为解决菱形问题,优先使用实现的顺序:只有当类实现其接口的所有默认/可选方法时,才能编译代码并使用此类的实现.否则,编译器会尝试使用接口的默认实现来修补缺少的实现.如果方法有多个默认实现,则会发生菱形问题,编译器会拒绝编译.此外,如果类实现接口的默认方法,则将使用该类的实现而不是接口的默认实现.

如果你坚持使用抽象类,你将永远不会遇到这个问题.但是,如果您的对象需要实现两个接口(因为它需要添加到期望这些类型的列表中,例如),并且这些接口具有冲突的方法签名,您将最终重新定义一大堆方法甚至如果这意味着你只是在进行超级调用,那么这会破坏基于继承的动态调度点.它不是一个交易破坏者,但在构建复杂的Java项目时需要考虑