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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28681737/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 13:59:01  来源:igfitidea点击:

Java 8 default methods as traits : safe?

javajava-8traitsdefault-method

提问by youri

Is it a safe practice to use default methods as a poor's man version of traitsin Java 8?

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

Some claim it may make pandas sadif 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.

有人声称如果你只是为了它而使用它们可能会让Pandas伤心,因为它很酷,但这不是我的意图。也经常有人提醒,默认方法是为了支持 API 演化和向后兼容性而引入的,这是事实,但这并不会导致将它们用作特征本身是错误的或扭曲的。

I have the following practical use casein mind:

我有以下实际用例

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

Or perhaps, define a PeriodTrait:

或者,定义一个PeriodTrait

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

Admitedly, composition could be used (or even helper classes) but it seems more verbose and cluttered and does not allow to benefit from polymorphism.

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

So, is it ok/safe to use default methods as basic traits, or should I be worried about unforeseen side effects?

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

Several questionson SO are related to Java vs Scala traits; that's not the point here. I'm not asking merely for opinions either. Instead, I'm looking for an authoritative answer or at least field insight: if you've used default methods as traits on your corporate project, did it turn out to be a timebomb?

关于 SO 的几个问题与 Java 与 Scala 特征有关;这不是重点。我也不仅仅是征求意见。相反,我正在寻找权威的答案或至少是现场洞察力:如果您在公司项目中使用默认方法作为特征,结果是定时炸弹吗?

回答by Brian Goetz

The short answer is: it's safe if you use them safely :)

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

The snarky answer: tell me what youmean by traits, and maybe I'll give you a better answer :)

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

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.

严肃地说,“特质”一词并没有明确定义。许多 Java 开发人员最熟悉 Scala 中表达的特征,但 Scala 远不是第一种具有特征的语言,无论是名称还是效果。

For example, in Scala, traits are stateful (can have varvariables); 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 中,traits 是有状态的(可以有var变量);在堡垒中,他们是纯粹的行为。Java 的默认方法接口是无状态的;这是否意味着它们不是特征?(提示:这是一个棘手的问题。)

Again, in Scala, traits are composed through linearization; if class Aextends traits Xand Y, then the order in which Xand Yare mixed in determines how conflicts between Xand Yare resolved. In Java, this linearization mechanism is not present (it was rejected, in part, because it was too "un-Java-like".)

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

The proximate reason for adding default methods to interfaces was to support interface evolution, but we were well aware that we were going beyond that. Whether you consider that to be "interface evolution++" or "traits--" is a matter of personal interpretation. So, to answer your question about safety ... so long as you stick to what the mechanism actually supports, rather than trying to wishfully stretch it to something it does not support, you should be fine.

为接口添加默认方法的最直接原因是支持接口演化,但我们很清楚我们正在超越它。无论您认为这是“界面进化++”还是“特征--”,这都是个人解释的问题。因此,要回答您关于安全性的问题……只要您坚持该机制实际支持的内容,而不是一厢情愿地将其扩展到它不支持的内容,您应该没问题。

A key design goal was that, from the perspective of the clientof an interface, default methods should be indistinguishable from "regular" interface methods. The default-ness of a method, therefore, is only interesting to the designerand implementorof the interface.

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

Here are some use cases that are well within the design goals:

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

  • Interface evolution. Here, we are adding a new method to an existing interface, which has a sensible default implementation in terms of existing methods on that interface. An example would be adding the forEachmethod to Collection, where the default implementation is written in terms of the iterator()method.

  • "Optional" methods. Here, the designer of an interface is saying "Implementors need not implement this method if they are willing to live with the limitations in functionality that entails". For example, Iterator.removewas given a default which throws UnsupportedOperationException; since the vast majority of implementations of Iteratorhave this behavior anyway, the default makes this method essentially optional. (If the behavior from AbstractCollectionwere expressed as defaults on Collection, we might do the same for the mutative methods.)

  • Convenience methods. These are methods that are strictly for convenience, again generally implemented in terms of non-default methods on the class. The logger()method in your first example is a reasonable illustration of this.

  • Combinators. These are compositional methods that instantiate new instances of the interface based on the current instance. For example, the methods Predicate.and()or Comparator.thenComparing()are examples of combinators.

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

  • “可选”方法。在这里,接口的设计者说“如果实现者愿意忍受所带来的功能限制,他们就不需要实现这个方法”。例如,Iterator.remove给出了一个默认值,抛出UnsupportedOperationException; 因为Iterator无论如何绝大多数实现都有这种行为,默认情况下这个方法本质上是可选的。(如果 from 的行为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 @implSpecjavadoc 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.

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