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中,特征是有状态的(可以有var
variables); 在堡垒他们是纯粹的行为。 Java与默认方法的接口是无状态的; 这是否意味着他们不是特质? (提示:这是一个诡计的问题。)
同样,在斯卡拉,性状是通过线性化构成的; 如果A
类扩展特征X
和Y
,那么X
和Y
混合的顺序决定了X
和Y
之间的冲突如何解决。 在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标签来实现这一点)来帮助实现者理解他们是否想要重写方法。 一些默认设置,如便捷方法和组合器,几乎不会被覆盖。 其他人,如可选方法,往往被忽略。 您需要提供足够的规范(而不仅仅是文档)来说明默认的承诺,因此实现者可以对是否需要覆盖它做出明智的决定。