为什么Java中的@FunctionalInterface没有@ConformsTo?

时间:2018-01-25 15:32:33

标签: java lambda api-design functional-interface

Java(1.8+)有一个@FunctionalInterface注释,它(基本上)建议您可以将方法引用代替Interface实现传递给另一个方法调用。我今天玩的有用的是:

DateTimeFormatter.parse(String, TemporalQuery<T>)

这很好,因为它可以让你告诉格式化程序什么样的结果交给你。 javadoc甚至给你一个很好的例子:

The query is typically a method reference to a from(TemporalAccessor) method. For example: 
  LocalDateTime dt = parser.parse(str, LocalDateTime::from);

一旦我了解了@FunctionalInterface是什么意思,我就开始想知道API的消费者是如何弄清楚他们实际可以使用什么的。上面的示例告诉您可以使用的内容,如果您浏览java.time包,则可以找到可以使用的其他方法引用。但是,API的任何贡献者都需要读取整个javadoc以确保它们不会破坏其他地方提到的任何隐式契约(当然它们应该,特别是对于JDK,但这不是javadoc的目的!)< / p>

所以..如果这个API的贡献者要更改LocalDateTime :: from的签名,那么就没有编译时检查这个方法不再符合'TemporalQuery'的FuncitonalInterface。这显然会破坏API的任何消费者,他们可以改变他们的代码以使用显式的lambda。我确实理解它不需要,但是如果注释类似于可选的'@Override'注释,那么它将提供一些编译时检查以及内省/反射以发现可用方法引用的可能性。

e.g。

@ConformsTo(TemporalQuery.class)
public static LocalDateTime from(TemporalAccessor temporal)

然后还可以通过内省找到可用于FunctionalInterface的任何其他方法引用。

所以,要明确的是,我知道这不是必要的,但是认为这似乎是一种疏忽,不将其作为可选的注释包含在内。这可能/不应该存在任何特殊原因吗?

1 个答案:

答案 0 :(得分:1)

更改方法的签名或返回类型所引起的问题,例如LocalDateTime :: from不限于功能接口。甚至在Java 8改变之前,就有可能破坏依赖于这些东西的现有代码。这就是为什么设计API始终是一个挑战,因为对现有代码的更改可能意味着很多工作。

此外,假设功能接口和匹配方法是不同库的一部分,你真的希望它们是紧密耦合的,即当一个变化时需要改变吗?如果他们由不同的组织维护(比如说不同的开源项目或公司)会怎样 - 他们应该如何协调?

Comparator.comparing(Function<? super T, ? extends U> keyExtractor)为例。这基本上接受对任何不带参数的方法的引用,并返回可比的东西。有这么多的库已经提供了这些方法,您是否希望它们都必须添加@ConformsTo

尽管如此,@ConformsTo充其量只是不完整,甚至可能会误导/过时。

修改

让我们从编译器的角度处理这两个注释。

@FunctionalInterface告诉编译器,当你定义多个抽象方法或者在接口以外的其他方法上使用它时,它应该会抱怨。

这意味着需求/合同定义(“此接口是一个功能接口”)和实现(接口本身)包含在同一个文件中,因此无论如何都必须一起更改。

@ConformsTo可以告诉编译器检查功能接口(甚至接口)的要求,看看该方法是否满足它们。

到目前为止一切都那么好,但是当接口发生变化时会出现问题:它会将方法和接口耦合在一起,这可能是不同的,也可能是完全不相关的库的一部分。即使它们是同一个库的一部分,当方法本身不会被重新编译时,你可能会遇到问题 - 编译器可能会错过这种不兼容性,从而违背了该注释的目的(如果它只针对人类那么简单评论也足够了。