18

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

java.util.function 包中有43个带有这个注解的接口。搜索 jdk.1.8.0/src@FunctionalInterface只找到 57 个命中。为什么其他可以添加的接口(如 AutoCloseable)@FunctionalInterface仍然缺少它?

注释文档中有一些模糊的提示:

“用于指示接口类型声明旨在成为功能接口的信息注释类型”

有什么好的理由不打算不将我设计的接口(可能只是一个功能接口)用作一个接口吗?除了没有意识到它可以被添加之外,它是否意味着任何东西?

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

更新:查看“‘可比’应该是‘功能界面’吗?” 我发现我仍然有一些唠叨的问题。当单一方法接口和功能接口在结构上相同时,还有什么不同之处?仅仅是名字的区别吗?Comparable 和 Comparator 在语义上足够接近。事实证明它们在结构上是不同的,但仍然不是最好的例子......

是否存在这样的情况,即 SMI 在结构上可以用作功能接口,但仍然不鼓励接口名称和方法的语义含义?或者可能是 Javadocs 所暗示的合同?

4

3 回答 3

24

好吧,如果您假设始终存在该意图,那么记录意图的注释将毫无用处。

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

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


interface不使用@FunctionalInterface示例标记的可能原因:

  • 具有interface编程语言语义,例如AutoClosableIterable(对于您自己的接口不太可能发生)
  • 预计不会interface有任意实现和/或比实际实现更多的标识符,例如java.net.ProtocolFamily,或java.lang.reflect.GenericArrayType(请注意,后者也将继承一个default实现,getTypeName()因为依赖于 lambda 实现是无用的toString()
  • this 的实例interface应该有一个标识,例如java.net.ProtocolFamily,java.nio.file.WatchEvent.Modifier等。请注意,这些通常由一个enum

    另一个例子是java.time.chrono.Era它恰好只有一个abstract方法,但它的规范说“Era可以使用==运算符比较实例。”</p>

  • interface旨在改变操作的行为,对于该操作的实现没有interface继承/实现其他任何东西没有意义,例如java.rmi.server.Unreferenced
  • 它是类的常见操作的抽象,它应该不仅仅是这些操作,例如java.io.Closeable, java.io.Flushable,java.lang.Readable
  • 预期的继承是合同的一部分,并且禁止 lambda 表达式实现,例如在java.awt:ActiveEvent应该由 a 实现AWTEventPrinterGraphics由 a实现Graphics,同样适用于java.awt.print.PrinterGraphics(嘿,两个interfaces 表示完全相同的东西……),而javax.print.FlavorException应该由javax.print.PrintException子类实现
  • 我不知道各种事件侦听器接口是否没有标记@FunctionalInterface为与其他不能成为功能接口的多方法事件侦听器对称,但实际上事件侦听器是 lambda 表达式的良好候选者。如果您想稍后删除一个侦听器,您必须存储该实例,但这与例如内部类侦听器实现没有什么不同。
  • 库维护者拥有一个包含 200 多种候选类型的大型代码库,并且没有资源来讨论interface是否应该对其进行注释,因此专注于在功能上下文中使用的主要候选类型。我敢肯定,例如java.io.ObjectInputValidation, java.lang.reflect.InvocationHandler, juc RejectedExecutionHandler&ThreadFactory不会很差,@FunctionalInterface但我不知道 eg 是否java.security.spec.ECField是一个好的候选人。库越通用,库的用户就越有可能回答interface他们感兴趣的特定问题,但是坚持要求库维护者为所有接口回答这个问题是不公平的。

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

于 2015-01-27T08:44:03.460 回答
2

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

于 2015-01-27T08:43:55.550 回答
0

在 java 8 中,函数式接口是一个接口,它只有一个抽象方法,称为函数式方法,lambda 表达式的参数和返回类型与之匹配。

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 定义的所有标准。

于 2018-01-28T12:08:21.170 回答