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

时间:2015-02-23 19:25:36

标签: java java-8 traits default-method

在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) {
        ...
    }
}

无可否认,可以使用组合(甚至辅助类),但它看起来更冗长,更混乱,不允许从多态中受益。

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

SO上的{p> Several questions与Java vs Scala特征有关;这不是重点。我也不仅仅是在征求意见。相反,我正在寻找一个权威的答案或者至少是现场洞察力:如果你在公司项目中使用默认方法作为特征,那么它是否真的是一个定时炸弹?

1 个答案:

答案 0 :(得分:112)

简短的回答是:如果你安全地使用它,那就安全了:)

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

严肃地说,术语" trait"没有明确的定义。许多Java开发人员最熟悉特征,因为它们在Scala中表达,但Scala远非第一种具有特征的语言,无论是名称还是有效。

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

同样,在Scala中,特征是通过线性化组成的;如果类A扩展了特征XY,那么XY混合的顺序决定了XY之间的冲突forEach已解决。在Java中,这种线性化机制不存在(它被部分拒绝了,因为它太过"非类似Java&#34 ;.)

向接口添加默认方法的近似原因是支持接口演进,但我们很清楚我们已经超越了这一点。你是否认为这是"接口evolution ++"或者"特征 - "是一个个人解释的问题。所以,回答你关于安全性的问题......只要你坚持机制实际支持的内容,而不是试图将它扩展到它不支持的东西,你应该没问题。

一个关键的设计目标是,从界面的客户端的角度来看,默认方法应该与"常规"无法区分。界面方法。因此,方法的默认值仅对接口的设计器实现者感兴趣。

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

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

  • "可选"方法。在这里,界面的设计者说"如果他们愿意忍受需要的功能限制,那么实现者不需要实现这种方法。例如,UnsupportedOperationException被赋予默认值Iterator;因为绝大多数AbstractCollection的实现都有这种行为,所以默认情况下这个方法基本上是可选的。 (如果来自Collection的行为在logger()上表示为默认值,我们可能会对变异方法执行相同操作。)

  • 便利方法。这些方法严格来说是为了方便,通常也是根据类中的非默认方法实现的。第一个示例中的Predicate.and()方法是对此的合理说明。

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

如果你提供了一个默认实现,你还应该为默认实现提供一些规范(在JDK中,我们使用{{1}} javadoc标签)来帮助实现者理解他们是否要覆盖方法或不。一些默认值,如方便方法和组合器,几乎从不被覆盖;其他人,如可选方法,经常被覆盖。您需要提供有关默认承诺要做的足够的规范(不仅仅是文档),因此实现者可以做出明智的决定,决定是否需要覆盖它。