使用 是否安全?默认方法作为特征的穷人版本 在 Java 8 中?
Some claim it may make pandas sad如果你只是为了它而使用它们,因为它很酷,但这不是我的意图。也经常有人提醒,默认方法是为了支持 API 演化和向后兼容而引入的,这是事实,但这并不意味着将它们用作特征本身是错误的或扭曲的。
我有 the following practical use case心里:
public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}
或者,定义一个
PeriodTrait
:public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}
诚然,可以使用组合(甚至是辅助类),但它似乎更加冗长和困惑,并且不允许从多态中受益。
所以,使用默认方法作为基本特征是否可以/安全 ,还是我应该担心无法预料的副作用?
Several questions在 SO 上与 Java 与 Scala 特征有关;这不是重点。我也不仅仅是征求意见。相反,我正在寻找权威的答案或至少是现场洞察力:如果您在公司项目中使用默认方法作为特征,结果是定时炸弹吗?
最佳答案
简短的回答是:如果你安全地使用它们是安全的:)
尖刻的回答:告诉我你所说的特征是什么意思,也许我会给你一个更好的答案:)
严肃地说,“特质”一词并没有明确定义。许多 Java 开发人员最熟悉 Scala 中表达的特征,但 Scala 远不是第一种具有特征的语言,无论是名称还是效果。
例如,在 Scala 中,traits 是有状态的(可以有 var
变量);在堡垒中,他们是纯粹的行为。 Java 的默认方法接口(interface)是无状态的;这是否意味着它们不是特征? (提示:这是一个棘手的问题。)
同样,在 Scala 中,特征是通过线性化组合而成的;如果类A
扩展特性 X
和 Y
,那么X
的顺序和 Y
混入决定了X
之间如何冲突和 Y
得到解决。在 Java 中,这种线性化机制不存在(它被拒绝了,部分原因是它太“不像 Java”了。)
为接口(interface)添加默认方法的最直接原因是支持接口(interface)演化,但我们很清楚我们正在超越这一点。无论您认为这是“界面进化++”还是“特征--”,这都是个人解释的问题。因此,要回答您关于安全性的问题……只要您坚持该机制实际支持的内容,而不是一厢情愿地将其扩展到它不支持的内容,您应该没问题。
一个关键的设计目标是,从接口(interface)客户端的角度来看,默认方法应该与“常规”接口(interface)方法没有区别。因此,方法的默认值只对接口(interface)的设计者和实现者感兴趣。
以下是一些完全符合设计目标的用例:
forEach
方法 Collection
,其中默认实现是根据 iterator()
编写的方法。 Iterator.remove
给出了一个默认值,抛出 UnsupportedOperationException
;因为 Iterator
的绝大多数实现无论如何都有这种行为,默认情况下该方法本质上是可选的。 (如果来自 AbstractCollection
的行为被表达为 Collection
的默认值,我们可能对可变方法做同样的事情。)logger()
您的第一个示例中的方法是对此的合理说明。 Predicate.and()
或 Comparator.thenComparing()
是组合子的例子。 如果您提供默认实现,您还应该为默认提供一些规范(在 JDK 中,我们为此使用
@implSpec
javadoc 标记)以帮助实现者了解他们是否要覆盖该方法。一些默认值,如便利方法和组合器,几乎从未被覆盖;其他的,比如可选的方法,经常被覆盖。您需要提供关于默认 promise 做什么的足够规范(不仅仅是文档),以便实现者可以就他们是否需要覆盖它做出明智的决定。
关于Java 8 默认方法作为特征 : safe?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28681737/