Java 8默认方法作为特征:安全吗?

在Java 8中使用默认方法作为穷人的特征版本是否安全?

有人说,如果你为了这个目的而使用它们,可能会让pandas伤心 ,因为它很酷,但那不是我的意图。 也常常提醒我们,引入默认方法来支持API进化和向后兼容,这是真实的,但这并不是错误的或扭曲的使用它们本身。

记住下面的实际用例 :

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

或者,也许定义一个PeriodTrait

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

有意思的是,可以使用组合(甚至是辅助类),但是它看起来更冗长和混乱,不能从多态性中受益。

那么, 使用默认方法作为基本特征可以吗?或者我应该担心不可预见的副作用?

关于SO的几个问题与Java和Scala特性有关; 这不是重点。 我也不只是要求意见。 相反,我正在寻找一个权威的答案或至less是现场的见解:如果你已经使用默认方法作为企业项目的特征,那么它是否会变成一个时间炸弹?

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

狡猾的答案:告诉我你的意思是什么特质,也许我会给你一个更好的答案:)

严肃地说,“特质”一词不是很清楚。 许多Java开发人员最熟悉Scala中expression的特征,但Scala远不是具有特征的第一语言,无论是名称还是效果。

例如,在Scala中,特征是有状态的(可以有varvariables); 在堡垒他们是纯粹的行为。 Java与默认方法的接口是无状态的; 这是否意味着他们不是特质? (提示:这是一个诡计的问题。)

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

向接口添加默认方法的近似原因是为了支持接口的演变 ,但是我们清楚地意识到我们正在超越这个接口。 不pipe你认为是“界面进化++”还是“特征 – ”都是个人解释的问题。 所以,要回答你关于安全的问题……只要你坚持这个机制实际上支持的东西,而不是试图把它延伸到它不支持的东西,你应该没问题。

一个关键的devise目标是,从接口客户端的angular度来看,默认方法应该与“常规”接口方法无法区分。 因此,一个方法的默认性只对接口的devise者实现者有意义。

以下是一些符合devise目标的用例:

  • 接口进化。 在这里,我们正在为现有接口添加一个新方法,该方法在接口上的现有方法上有一个合理的默认实现。 一个例子是将forEach方法添加到Collection ,默认实现是用iterator()方法编写的。

  • “可选”方法。 在这里,一个接口的devise者说:“如果他们愿意忍受function的限制,那么实现者不需要实现这个方法”。 例如, Iterator.remove被赋予了一个引发UnsupportedOperationException的默认值; 因为无论如何, Iterator的绝大多数实现都有这种行为,默认情况下,这个方法是可选的。 (如果AbstractCollection中的行为被表示为Collection默认值,那么我们可能会为这个变种方法做同样的事情。)

  • 便利的方法。 这些方法严格地说是为了方便,同样也是通常以类的非默认方法来实现的。 第一个例子中的logger()方法就是一个合理的例子。

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

如果你提供了一个默认的实现,你还应该提供一些默认的规范(在JDK中,我们使用@implSpec javadoc标签来实现这一点)来帮助实现者理解他们是否想要重写方法。 一些默认设置,如便捷方法和组合器,几乎不会被覆盖。 其他人,如可选方法,往往被忽略。 您需要提供足够的规范(而不仅仅是文档)来说明默认的承诺,因此实现者可以对是否需要覆盖它做出明智的决定。