Java 函数式接口,用户希望使用现有的函数式接口,或编写自定义函数式接口,创建只包含单一抽象方法的接口,并为其添加 @FunctionalInterface
注解。Java 8 引入的函数式接口是一种包含单一抽象方法的接口,因此可以作为 lambda 表达式或方法引用的目标。
Java 函数式接口 问题描述
用户希望使用现有的函数式接口,或编写自定义函数式接口。
Java 函数式接口 解决方案
创建只包含单一抽象方法的接口,并为其添加 @FunctionalInterface
注解。
Java 函数式接口 具体实例
Java 8 引入的函数式接口是一种包含单一抽象方法的接口,因此可以作为 lambda 表达式或方法引用的目标。
关键字 abstract
在这里很重要。在 Java 8 之前,接口中的所有方法被默认视为抽象方法,不需要为它们添加 abstract
。
我们定义一个名为 PalindromeChecker
(回文检查器)的接口,如例 1-22 所示。
例 1-22 回文检查器接口
@FunctionalInterface
public interface PalindromeChecker {
boolean isPalidrome(String s);
}
由于接口中的所有方法均为 public
方法4,可以省略访问修饰符,如同省略 abstract
关键字一样。
4至少在 Java 9 之前,接口中也允许使用 private
方法。更多详细信息,参见范例 接口中的私有方法 。
由于 PalindromeChecker
仅包含一个抽象方法,它属于函数式接口。Java 8 在 java.lang
包中提供了 @FunctionalInterface
注解,可以应用到函数式接口,如例 1-22 所示。
@FunctionalInterface
注解并非必需,但使用它是一种好习惯,原因有两点。首先,@FunctionalInterface
注解会触发编译时校验(compile-time check),有助于确保接口符合要求。如果接口不包含或包含多个抽象方法,程序将提示编译错误。
其次,添加 @FunctionalInterface
注解后,会在 Javadoc 中生成以下语句:
Functional Interface:
This is a functional interface and can therefore be used as the assignment
target for a lambda expression or method reference.
函数式接口中同样可以使用 default
和 static
方法。由于这两种方法都有相应的实现,它们与“仅包含一个抽象方法”的要求并不矛盾。示例代码如例 1-23 所示。
例 1-23
MyInterface
是一个包含静态方法和默认方法的函数式接口
@FunctionalInterface
public interface MyInterface {
int myMethod(); ➊
// int myOtherMethod(); ➋
default String sayHello() {
return "Hello, World!";
}
static void myStaticMethod() {
System.out.println("I'm a static method in an interface");
}
}
❶ 单一抽象方法
❷ 如果这条语句未被注释掉,MyInterface
将不再是函数式接口
可以看到,如果 myOtherMethod
方法未被注释掉,MyInterface
就不再满足函数式接口的要求,@FunctionalInterface
注解将报错:“存在多个非重写的抽象方法。”
接口可以继承其他接口(甚至不止一个)。@FunctionalInterface
注解将对当前接口进行校验。因此,如果一个接口继承现有的函数式接口后,又添加了其他抽象方法,该接口就不再是函数式接口,如例 1-24 所示。
例 1-24 继承函数式接口的
MyChildInterface
不再属于函数式接口
public interface MyChildInterface extends MyInterface {
int anotherMethod(); ➊
}
➊ 其他抽象方法
MyChildInterface
不属于函数式接口,因为它包含两个抽象方法:继承自 MyInterface
的 myMethod
和声明的 anotherMethod
。即便没有添加 @FunctionalInterface
注解,代码也可以编译,因为 MyChildInterface
就是一个标准接口,但无法作为 lambda 表达式的目标。
此外,还有一种不太常见的情况值得注意。Comparator
接口用于排序,其他范例将对此进行讨论。查看 Comparator
接口的 Javadoc 信息,并点击 Abstract Methods(抽象方法)标签后,将显示以下信息(图 1-1)。
图 1-1:Comparator
接口包含的抽象方法
可以看到,Comparator
接口包含两种抽象方法,且其中一种方法是在 java.lang.Object
类中实际实现的,那么 Comparator
为什么还属于函数式接口呢?
这里的特别之处在于,图中显示的 equals
方法来自 Object
类,因此已有一个默认的实现。根据 Javadoc 的描述,出于性能方面的考虑,用户可以提供满足相同契约(interface contract)的自定义 equals
方法,但不对该方法进行重写“始终是安全的”。
根据函数式接口的定义,Object
类中的方法与单一抽象方法的要求并不矛盾,因此 Comparator
仍然属于函数式接口。