Java8默认方法作为 trait: 安全吗?

在 Java8中使用 默认的方法作为穷人版本的特征是一种安全的做法吗?

Some claim it may make pandas sad if you use them just for the sake of it, because it's cool, but that's not my intention. It is also often reminded that default methods were introduced to support API evolution and backward compatibility, which is true, but this does not make it wrong or twisted to use them as traits per se.

我想到了 下面的实际用例:

public interface Loggable {
default Logger logger() {
return LoggerFactory.getLogger(this.getClass());
}
}

或者,定义一个 PeriodTrait:

public interface PeriodeTrait {
Date getStartDate();
Date getEndDate();
default isValid(Date atDate) {
...
}
}

诚然,可以使用组合(甚至是 helper 类) ,但它似乎更加冗长和混乱,不允许从多态性中获益。

那么,是 使用默认方法作为基本特征是否可行/安全还是我应该担心不可预见的副作用呢?

关于 SO 的几个问题与 Java vs Scala trait 有关; 这不是重点。我也不只是征求意见。相反,我正在寻找一个权威的答案,或者至少是一个领域的洞察力: 如果你在你的公司项目中使用默认的方法作为特性,那么它是一个定时炸弹吗?

12701 次浏览

简短的回答是: 如果你安全地使用它们是安全的:)

尖刻的回答是: 告诉我 中的 trait 是什么意思,也许我会给你一个更好的答案:)

In all seriousness, the term "trait" is not well-defined. Many Java developers are most familiar with traits as they are expressed in Scala, but Scala is far from the first language to have traits, either in name or in effect.

For example, in Scala, traits are stateful (can have var variables); in Fortress they are pure behavior. Java's interfaces with default methods are stateless; does this mean they are not traits? (Hint: that was a trick question.)

同样,在 Scala 中,trait 是通过线性化组合的; 如果类 A扩展了 trait XY,那么 XY混合的顺序决定了 XY之间的冲突是如何解决的。在 Java 中,这种线性化机制不存在(它被拒绝了,部分原因是它太“不像 Java”了)

将默认方法添加到接口的直接原因是为了支持 interface evolution,但是我们很清楚我们正在超越这一点。你是否认为这是“界面进化 + +”或“特质——”是一个个人解释的问题。所以,为了回答你关于安全性的问题... ... 只要你坚持这个机制实际上支持的东西,而不是一厢情愿地把它延伸到它不支持的东西,你就应该没事。

一个关键的设计目标是,从接口的 客户的角度来看,缺省方法应该与“常规”接口方法无法区分。因此,方法的默认性只对接口的 设计师执行者感兴趣。

下面是一些完全符合设计目标的用例:

  • 界面进化。在这里,我们将向现有接口添加一个新方法,根据该接口上的现有方法,该接口具有合理的默认实现。一个例子是将 forEach方法添加到 Collection,其中默认实现是根据 iterator()方法编写的。

  • 「可选」方法。在这里,接口的设计者说: “如果实现者愿意忍受所带来的功能限制,他们就不需要实现这个方法”。例如,Iterator.remove被赋予了一个默认值,它抛出 UnsupportedOperationException; 由于 Iterator的绝大多数实现都有这种行为,默认值使得这个方法基本上是可选的。(如果来自 AbstractCollection的行为在 Collection上表示为默认值,那么我们可以对变异方法执行相同的操作。)

  • 方便的方法。这些方法只是为了方便起见,通常也是用类上的非默认方法来实现的。第一个示例中的 logger()方法就是对此的合理说明。

  • 组合器。这些组合方法基于当前实例实例化接口的新实例。例如,方法 Predicate.and()Comparator.thenComparing()是组合子的例子。

If you provide a default implementation, you should also provide some specification for the default (in the JDK, we use the @implSpec javadoc tag for this) to aid implementors in understanding whether they want to override the method or not. Some defaults, like convenience methods and combinators, are almost never overridden; others, like optional methods, are often overridden. You need to provide enough specification (not just documentation) about what the default promises to do, so the implementor can make a sensible decision about whether they need to override it.