Java 函数式接口

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.

函数式接口中同样可以使用 defaultstatic 方法。由于这两种方法都有相应的实现,它们与“仅包含一个抽象方法”的要求并不矛盾。示例代码如例 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 不属于函数式接口,因为它包含两个抽象方法:继承自 MyInterfacemyMethod 和声明的 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 仍然属于函数式接口。

赞(5)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

Java 实例