为什么不在 JDK 中所有符合条件的接口上使用 @FunctionalInterface?

Why isn't @FunctionalInterface used on all the interfaces in the JDK that qualify?

Java 8 为我们提供了许多有趣的方法来使用函数式接口以及一个新的注解:@FunctionalInterface。它的工作是告诉编译器如果我们不遵守功能接口的规则(请只需要覆盖一个抽象方法)就对我们大喊大叫。

个带有此注释。在 jdk.1.8.0/src 中搜索 @FunctionalInterface 仅出现 57 次匹配。为什么其他可以添加的接口(例如 AutoCloseable)@FunctionalInterface 仍然没有添加?

annotations documentation中有一点模糊的提示:

"An informative annotation type used to indicate that an interface type declaration is intended to be a functional interface"

有什么充分的理由不打算我设计的界面(可能只是碰巧是一个功能界面)不被用作一个界面?除了没有意识到它可以被添加之外,是否将其关闭表明有任何迹象?

向任何已发布的接口添加抽象方法是否会搞砸任何实现它的人,无论功能与否?假设他们只是懒得去追捕他们,我感到愤世嫉俗,但还有什么其他解释?

更新: 看完"Should 'Comparable' be a 'Functional interface'?",我发现我还有一些问题。当单一方法接口和功能接口在结构上相同时,还有什么不同之处呢?区别仅仅是名字吗? Comparable 和 Comparator 在语义上足够接近。事实证明它们在结构上有所不同,但仍然不是最好的例子......

是否存在这样一种情况:SMI 在结构上可以很好地用作功能接口,但仍然不鼓励接口名称和方法名称的语义?或者可能是 Java 文档隐含的合同?

计划扩张。仅仅因为接口现在符合 SMI 的要求并不意味着以后不需要扩展。

好吧,如果您假设总是给出该意图,那么记录意图的注释将毫无用处。

您将示例命名为 AutoCloseable,这显然 不是 打算作为函数实现的,因为 Runnable 对于函数来说更方便带有 ()->void 签名。它的目的是 class 实现 AutoCloseable 管理外部资源,而通过 lambda 表达式实现的匿名 classes 则不会。

一个更清楚的例子是Comparable,一个interface不仅不打算作为lambda表达式来实现,而且使用lambda表达式也不可能正确地实现它。


未将 interface 标记为 @FunctionalInterface 的可能原因示例:

  • interface 具有编程语言语义,例如AutoClosableIterable(您自己的界面不太可能发生这种情况)
  • 预计 interface 不会有任意实现 and/or 与其说是实际实现,不如说是一个标识符,例如java.net.ProtocolFamilyjava.lang.reflect.GenericArrayType(请注意,后者还将继承 default 实现,因为 getTypeName() 对于依赖于 toString() 的 lambda 实现无用)
  • 这个 interface 的实例应该有一个标识,例如java.net.ProtocolFamilyjava.nio.file.WatchEvent.Modifier 等。请注意,这些通常由 enum

    实现

    另一个例子是 java.time.chrono.Era,它恰好只有一个 abstract 方法,但是 its specification 说“Era 的实例可以使用 == 运算符。”

  • interface 旨在改变一个操作的行为,在没有 inheriting/implementing 的情况下实现 interface 是没有意义的,例如java.rmi.server.Unreferenced
  • 它是对classes常用操作的抽象,它应该不止这些操作,例如java.io.Closeablejava.io.Flushablejava.lang.Readable
  • 预期的继承是契约的一部分,并禁止 lambda 表达式实现,例如在java.awt中:ActiveEvent应该由AWTEvent实现,PrinterGraphicsGraphics实现,同样适用于java.awt.print.PrinterGraphics(嘿,两个interfaces 对于完全相同的事情...),wheras javax.print.FlavorException 应该由 javax.print.PrintException subclass
  • 实现
  • 不知道是不是各种事件监听器接口没有标上@FunctionalInterface是为了和其他不能是函数式接口的多方法事件监听器对称,其实事件监听器是不错的选择对于 lambda 表达式。如果您想稍后删除侦听器,则必须存储实例,但这与例如内部 class 侦听器实现。
  • 库维护者拥有超过 200 种候选类型的大型代码库,没有资源来讨论每个 interface 是否应该注释,因此专注于成为的主要候选者在功能上下文中使用。我敢肯定,例如java.io.ObjectInputValidation, java.lang.reflect.InvocationHandler, juc RejectedExecutionHandler & ThreadFactory 不会像 @FunctionalInterface 那样糟糕,但我不知道是否,例如java.security.spec.ECField 是一个很好的候选人。图书馆越通用,图书馆的用户就越有可能针对他们感兴趣的特定 interface 回答该问题,但是坚持要求图书馆维护者回答 [=] 是不公平的80=]所有 接口。

    在这种情况下,将 @FunctionalInterface 的存在看作是一条消息,表明 interface 绝对旨在与 lambda 表达式一起使用,而不是将 @FunctionalInterface 的不存在视为更有意义注释作为指示符,因为它不打算以这种方式使用。这与编译器处理它的方式完全一样,您可以使用 lambda 表达式实现每个抽象方法 interface,但是当存在注释时,它将 确保 您可以使用它interface这样。

在java8中,函数式接口是只有一个抽象方法的接口,称为函数式方法,lambda表达式的参数和return类型匹配。

java.util.function 包含 JDK 使用的通用功能接口,也可供最终用户使用 。虽然它们不是 lambda 表达式可能适用的完整功能接口集,但它们提供的功能足以满足常见需求。当现有集不够时,您可以自由创建自己的功能接口。

有许多这样的接口值得被指定为功能接口,但是java.util.function包已经为我们几乎所有的目的提供了功能接口。

例如查看以下代码。

public interface Comparable<T> {
   public int compareTo(T o);
}

@FunctionalInterface
public interface ToIntFunction<T> {
   int applyAsInt(T value);
}

public static void main(String[] args){
   ToIntFunction<String> f = str -> Integer.parseInt(str);
   Comparable<String> c = str -> Integer.parseInt(str);
}

Comparable 也可以获取一个对象并派生一些 int 类型的值,但是提供了一个更通用的专用接口 ToIntFunction 来执行此任务。 没有硬性规定所有应得的接口都应使用 @FunctionalInterface 注释,但要获得 lambda 功能的优势,接口应满足 FunctionalInterface 定义的所有标准。