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

Posted

技术标签:

【中文标题】Java 8 默认方法作为特征:安全吗?【英文标题】:Java 8 default methods as traits : safe? 【发布时间】:2015-04-25 05:54:27 【问题描述】:

在 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 特征有关;这不是重点。我也不仅仅是征求意见。相反,我正在寻找一个权威的答案或至少是现场洞察力:如果您在公司项目中使用默认方法作为特征,那么它会变成定时炸弹吗?

【问题讨论】:

在我看来,您可以从继承抽象类中获得相同的好处,而不必担心让 pandas 哭泣......我可以看到在接口中使用默认方法的唯一原因是你需要这个功能并且不能修改一堆基于接口的遗留代码。 我同意@infosec812 关于扩展定义自己的静态记录器字段的抽象类。您的 logger() 方法不会在每次调用时都实例化一个新的记录器实例吗? 对于日志记录,您可能需要查看 Projectlombok.org 及其@Slf4j 注释。 Related question: Why is “final” not allowed in Java 8 interface methods? 【参考方案1】:

简短的回答是:安全使用它们是安全的 :)

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

说真的,“特质”一词的定义并不明确。许多 Java 开发人员最熟悉 Scala 中表达的特征,但 Scala 远非第一种在名称或效果上具有特征的语言。

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

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

向接口添加默认方法的最接近的原因是为了支持接口进化,但我们很清楚我们正在超越这一点。无论您认为这是“界面进化++”还是“特征--”都是个人解释的问题。所以,回答你关于安全的问题……只要你坚持机制实际支持的东西,而不是一厢情愿地把它延伸到它不支持的东西,你应该没问题。

一个关键的设计目标是,从接口的客户端的角度来看,默认方法应该与“常规”接口方法没有区别。因此,方法的默认性只对接口的设计者实现者 感兴趣。

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

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

“可选”方法。在这里,接口的设计者说“如果实现者愿意忍受所需要的功能限制,他们就不需要实现这个方法”。例如,Iterator.remove 被赋予一个默认值,它会抛出 UnsupportedOperationException;由于Iterator 的绝大多数实现无论如何都具有这种行为,因此默认使该方法本质上是可选的。 (如果 AbstractCollection 的行为被表达为 Collection 的默认值,我们可能会对可变方法执行相同的操作。)

方便的方法。这些是严格为方便起见的方法,同样通常根据类上的非默认方法来实现。第一个示例中的logger() 方法就是一个合理的说明。

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

如果您提供默认实现,您还应该为默认提供一些规范(在 JDK 中,我们为此使用 @implSpec javadoc 标记)以帮助实现者了解他们是否要覆盖该方法。一些默认值,比如便捷方法和组合器,几乎从不被覆盖;其他的,比如可选方法,经常被覆盖。您需要提供足够的规范(不仅仅是文档)来说明默认承诺的作用,以便实现者可以就是否需要覆盖它做出明智的决定。

【讨论】:

感谢 Brian 的全面回答。现在我可以轻松地使用默认方法。读者:Brian Goetz 提供的更多关于接口演变和默认方法的信息可以在NightHacking Worldwide Lambdas 中找到。 谢谢@brian-goetz。根据您所说,我认为 Java 的默认方法更接近 Ducasse 等人的论文 (scg.unibe.ch/archive/papers/Duca06bTOPLASTraits.pdf) 中定义的传统特征的概念。 Scala“特质”对我来说似乎根本不像特质,因为它们有状态,在它们的组合中使用线性化,而且它们似乎也隐含地解决了方法冲突——而这些都是传统特质所没有的有。事实上,我会说 Scala 的特征更像是混合而不是特征。你怎么看? PS:我从来没有用 Scala 写过代码。 对于充当监听器的接口中的方法使用空的默认实现怎么样?侦听器实现可能只对侦听少数接口方法感兴趣,因此通过将方法设为默认值,实现者只需实现它需要侦听的方法。 @LahiruChandima 喜欢MouseListener?就这种 API 风格的意义而言,这适合“可选方法”桶。确保清楚地记录可选性! 是的。就像在MouseListener 中一样。感谢您的回复。

以上是关于Java 8 默认方法作为特征:安全吗?的主要内容,如果未能解决你的问题,请参考以下文章

Java8新特征之接口默认方法

Java-Web学习-Java基础-Java 8 新特性(局部)

JAVA中的成员变量不赋值默认是0吗?

Java 8新特性-接口定义增强

JAVA8 十大新特性

java8的常用的新特征