在 java.util.Function 中有 43 个接口。不能指望全部记住它们,但是如果记住了六个基本接口,就可以在需要它们时派生出其余的接口。基本接口操作于对象引用类型。
Operator 接口表示方法的结果和参数类型相同。
Predicate 接口表示其方法接受一个参数并返回一个布尔值。
Function 接口表示方法其参数和返回类型不同。
Supplier 接口表示一个不接受参数和返回值 (或“供应”) 的方法。
Consumer 表示该方法接受一个参数而不返回任何东西,本质上就是使用它的参数。
六种基本函数式接口概述如下:
接口 | 方法 | 示例 |
UnaryOperator<T> | T apply(T t) | String::toLowerCase |
BinaryOperator<T> | T apply(T t1, T t2) | BigInteger::add |
Predicate<T> | boolean test(T t) | Collection::isEmpty |
Function<T,R> | R apply(T t) | Arrays::asList |
Supplier<T> | T get() | Instant::now |
Consumer<T> | void accept(T t) | System.out::println |
在处理基本类型 int,long 和 double 的操作上,六个基本接口中还有三个变体。 它们的名字是通过在基本接口前加一个基本类型而得到的。 因此,例如,一个接受 int 的 Predicate 是一个 IntPredicate ,而一个接受两个long 值并返回一个 long 的二元运算符是一个 LongBinaryOperator 。 除 Function 接口变体通过返回类型进行了参数化,其他变体类型都没有参数化。 例如, LongFunction<int[]> 使用 long 类型作为参数并返回了 int [] 类型。
Function 接口还有九个额外的变体,当结果类型为基本类型时使用。 源和结果类型总是不同,因为从类型到它自身的函数是 UnaryOperator 。 如果源类型和结果类型都是基本类型,则使用带有 SrcToResult 的前缀Function ,例如 LongToIntFunction (六个变体)。如果源是一个基本类型,返回结果是一个对象引用,那么带有 ToObj 的前缀 Function ,例如 DoubleToObjFunction (三种变体)。
有三个包含两个参数版本的基本功能接口,使它们有意义: BiPredicate <T,U> , BiFunction <T,U,R> 和 BiConsumer <T,U> 。 也有返回三种相关基本类型的 BiFunction 变体: ToIntBiFunction <T,U> , ToLongBiFunction<T,U> 和 ToDoubleBiFunction <T,U> 。Consumer 有两个变量,它们带有一个对象引用和一个基本类型: ObjDoubleConsumer <T> , ObjIntConsumer <T> 和 ObjLongConsumer <T> 。 总共有九个两个参数版本的基本接口。
最后,还有一个 BooleanSupplier 接口,它是 Supplier 的一个变体,它返回布尔值。 这是任何标准函数式接口名称中唯一明确提及的布尔类型,但布尔返回值通过 Predicate 及其四种变体形式支持。 前面段落中介绍的 BooleanSupplier 接口和 42 个接口占所有四十三个标准功能接口。 无可否认,这是非常难以接受的,并且不是非常正交的。 另一方面,你所需要的大部分功能接口都是为你写的,而且它们的名字是经常性的,所以在你需要的时候不应该有太多的麻烦。
大多数标准函数式接口仅用于提供对基本类型的支持。 不要试图使用基本的函数式接口来装箱基本类型的包装类而不是基本类型的函数式接口。 虽然它起作用,但它违反了第 61 条中的建议:“优先使用基本类型而不是基本类型的包装类”。使用装箱基本类型的包装类进行批量操作的性能后果可能是致命的。