Java 8需要后卫(默认)方法[重复]
Posted
技术标签:
【中文标题】Java 8需要后卫(默认)方法[重复]【英文标题】:Java 8 Need of Defender (default) Methods [duplicate] 【发布时间】:2015-12-01 00:35:48 【问题描述】:为什么我们在 java 8 的接口中需要 Defender 方法,因为我们已经有了抽象类。我在互联网上找到了各种答案,例如:
添加外部功能
但是抽象类是用于部分抽象的,因为我们的接口实际上是一个纯抽象类,为什么它们是接口内的默认方法?
【问题讨论】:
@Siguza 这两者是同义词,而“默认”是官方的。 通常的答案(很容易找到)是他们需要向Collection
等接口添加方法;没有默认方法会破坏大量将实现collection
但不会破坏新方法的第三方类。
@VGR 不是骗子,但一个答案解释得很好
【参考方案1】:
防御者方法背后的主要原因是能够使用新功能扩展长期存在的接口,而不会破坏现有代码。特别是在 Java 8 的 Lamba 表达式中,他们在集合接口上引入了许多新方法,例如 Iterable.forEach。通过提供默认方法,实现Iterable
接口的现有类无需更改即可在Java 8 环境中使用。
【讨论】:
关于“不破坏现有代码”你错了。如果您向接口添加已在某些实现类中定义且与它们不兼容的方法,例如它具有不同的结果类型,您将破坏现有代码。伙计们,你听说过开放/封闭原则吗? Brian Goetz 和 Oracle 的其他人在设计如此厚实的语言功能时肯定没有听说过。【参考方案2】:通过将功能放在抽象基类中来共享功能的问题在于,一个类只能从一个基类派生。如果您想从多个基础继承功能,这是一个限制。
当您需要从已有基类的类中实现接口时,通过抽象基类共享功能也可能成为问题。在这种情况下,您根本无法派生新类,因为您必须选择两个基中的一个,而您可能需要两者。
默认方法优雅地解决了这个问题:将您的通用实现放入默认方法允许您不受限制地共享代码。
您可以将默认方法和继承抽象类之间的主要区别视为在实现相同接口的兄弟之间水平或垂直之间共享功能之间的区别从同一个基类继承的子类。
这是一个例子:考虑一个看起来像 JDBC 的ResultSet
的接口,它有两种访问同一列的方式——按名称和按索引。接口可以这样编码:
interface ResultSet2
int findColumn(String columnLabel);
String getString(int index);
long getLong(int index);
default long getLong(String columnLabel)
return getLong(findColumn(columnLabel));
default String getString(String columnLabel)
return getString(findColumn(columnLabel));
任何实现ResultSet2
的人都必须实现三个方法,并免费获得剩下的两个。他们可以选择提供替代实现,但这是可选的。
【讨论】:
感谢您的回答,我认为现在很清楚 :)【参考方案3】:最初的意图是与 C# 的扩展方法竞争。给定接口的核心方法,例如get(), set() in List
,可以定义和实现扩展方法(例如sort()
)。
Java 人认为最好在接口本身上声明这些方法,而不是在外部位置;以便这些方法可以被子类型覆盖,为每个子类型提供最佳实现。 (他们还争辩说这些方法应该由接口作者控制;这是一个软点)
虽然可以将默认方法添加到现有接口,但破坏现有 3rd 方子类型的风险很大,特别是对于像 List
这样具有大量子类型的非常古老的类型。因此,很少有默认方法被添加到现有的核心 Java API 中。见this question。
对于新的接口,默认方法对于 API 设计者来说是一个非常有价值的工具。你可以给一个接口添加很多方便的方法,例如Function.compose()
。子类型只需要实现抽象/核心方法,而不是默认方法(但如果他们愿意,他们可以)。
我不同意默认方法可以“进化”接口的想法。它们不会改变接口的核心语义,它们只是方便的方法(以实例方法的形式)。
并且在设计界面时应该预先仔细设计默认方法;如前所述,事后添加默认方法是非常冒险的。
C# 的扩展方法允许第 3 方添加便利方法;这很好,Java 以后没有理由不能引入类似的东西。
【讨论】:
以上是关于Java 8需要后卫(默认)方法[重复]的主要内容,如果未能解决你的问题,请参考以下文章