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

时间:2014-11-12 20:05:37

标签: inheritance interface abstract-class java-8

我试图想出一个完整的答案:

  

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

并在以下方面寻找验证/建议。

答案是,

  

"为其中一些提供实施。在具体课程之前   进来定义特定类型,一个抽象类,通常在下面   继承层次结构中的接口(与Java API中的许多示例一样)   实现并确定了接口定义的结构的一些常见方面。

     

使用抽象类的另一个好理由是类型之间存在明确的逻辑层次结构。   抽象类可用于组织该层次结构   通过作为一个抽象类而不是一个具体的强制,强制对象只在具体类上实例化   在这个层次结构的下方,它们是完全定义的,因此是有意义的。"

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

  

"自Java 8以来,接口可以定义default method实现。   为什么我不会在接口中编写默认方法而不是实现这些方法的抽象类?   "

答案是:

  

"   一个原因是:接口方法都是公共的,字段成员都是常量(final和amp; public)。   您可能希望限制方法的访问权限和/或使它们在非常量状态下运行。

     

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

     

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

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

请注意:我已经看过Java 8 default methods vs. non-abstract methods in abstract classesInterface with default methods vs Abstract class in Java 8以及其他一些有用的讨论。这个Q更像是为什么选择一个接口的抽象类而不是相反。

TIA。

4 个答案:

答案 0 :(得分:7)

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

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

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

答案 1 :(得分:7)

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

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

以下是甲骨文在这个问题上所说的话:

  

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

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

在本文中,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

  

为了解决钻石问题,优先顺序为a   使用实现:仅当类实现所有默认值/   其接口的可选方法,代码可以编译和   使用此类的实现。否则编译器会尝试   使用接口的默认值修补缺少的实现   实现。如果有多个默认实现   方法,然后发生钻石问题,编译器拒绝   汇编。此外,如果该类实现了接口的默认值   方法,将使用该类的实现来代替   接口的默认实现。

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

答案 2 :(得分:1)

简短回答:

  1. 一个班级可以只延伸一个班级。
  2. 一个类可以实现任意数量的接口。

答案 3 :(得分:0)

Abstract类与普通类共享更多相似之处,而interface表示没有内部状态的类公共API。

  • 抽象类可以有构造函数但接口不能。
  • 抽象类只能从一个类继承,并且可以实现接口,但接口只能扩展多个接口。

  • 抽象类属性也可以是瞬态/易变的

  • 抽象类方法也可以是final / static / synchronized / native

  • 接口方法也可以是默认或静态的实现。(不能同时是默认和静态。)