七的博客

Java8通用函数式接口分类以及命名规律

Java

Java8通用函数式接口分类以及命名规律

前段时间写过一篇【Java8Lambda表达式最佳实践】,主要内容还是讲述的 Lambda 表达式。 但是 Lambda 表达式的好伙伴还得是函数式接口,两个一起合作才能发挥出最大的效果。

函数式接口就是一个普通的 Java 接口,只不过接口中只有单个抽象的接口方法。 通过 Java 的语法糖,可以让 Java 更高阶的函数式编程模式,提升代码的灵活性。

不过 JDK 通用的接口数量稍微有点多,大概有 40 多个接口。 如果不进行分类以及归纳的话,这么多的接口很难记得住,更难以在实际项目中熟练进行运用了。

1. 通用函数式接口列表分类

这里在 Oracle 官网没有找到类似的列表,不过在 API 文档上可以看到一批列表。

API 文档地址: https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html

这个包位于 java.util.function , 从这篇 API 文档中我大概做了下分类,比较容易理解命名规则以及套路。通用接口可以归纳为下面这 5 类,分别起着不同的作用。

分类名称 中文描述 作用
Operator Interfaces 运算符接口 对一个或者多个入参进行运算 , 函数的返回值跟函数入参类型保持一致。
Predicate Interfaces 断言接口 这些接口通常用于对象进行过滤、筛选或测试等 , 返回一个布尔值。
Function Interfaces 功能接口 表示接收一个或多个参数 , 并返回一个结果的函数。
Consumer Interfaces 消费者接口 接收一个或多个入参, 但不返回任何结果的操作。
Supplier Interfaces 生产者接口 延迟生成或按需生成某个返回值给函数的调用方。

上面这个表格的作用最好可以牢记,这样在实际运用过程中才可以灵活选择使用。

2. 通用函数式接口列表

截止到目前为止,JDK8 大概提供了 40 多个 通用的函数式接口类,分布情况如下:

运算符接口 断言接口 功能接口 消费者接口 生产者接口
UnaryOperator<T> Predicate<T> Function<T,R> Consumer<T> Supplier<T>
BinaryOperator<T> BiPredicate<T,U> BiFunction<T,U,R> BiConsumer<T,U> BooleanSupplier
DoublePredicate DoubleFunction<R> DoubleConsumer DoubleSupplier
IntPredicate IntFunction<R> IntConsumer IntSupplier
LongPredicate LongFunction<R> LongConsumer LongSupplier
ToDoubleFunction<T> ObjDoubleConsumer<T>
ToIntFunction<T> ObjIntConsumer<T>
ToLongFunction<T> ObjLongConsumer<T>
DoubleToIntFunction
DoubleToLongFunction
IntToDoubleFunction
IntToLongFunction
LongToDoubleFunction
LongToIntFunction

观察上面的表格中的内容,可以发现命名都是有规律的。只要我们掌握了命名规律,就可以很快的推算的出对应的函数式接口名称。

此外官方也对命名做了很详细的解释。下一小节将针对命名规律进行总结。

3. 通用函数式接口命名解释

在这篇文档 https://docs.oracle.com/javase/8/docs/api/java/util/function/package-summary.html 的最下面部分,有一段关于命名规则的描述,截取出来如下:

The functional interfaces in this package follow an extensible naming convention, as follows:

  • There are several basic function shapes, including Function (unary function from T to R), Consumer (unary function from T to void), Predicate (unary function from T to boolean), and Supplier (nilary function to R).
  • Function shapes have a natural arity based on how they are most commonly used. The basic shapes can be modified by an arity prefix to indicate a different arity, such as BiFunction (binary function from T and U to R).
  • There are additional derived function shapes which extend the basic function shapes, including UnaryOperator (extends Function) and BinaryOperator (extends BiFunction).
  • Type parameters of functional interfaces can be specialized to primitives with additional type prefixes. To specialize the return type for a type that has both generic return type and generic arguments, we prefix ToXxx, as in ToIntFunction. Otherwise, type arguments are specialized left-to-right, as in DoubleConsumer or ObjIntConsumer. (The type prefix Obj is used to indicate that we don’t want to specialize this parameter, but want to move on to the next parameter, as in ObjIntConsumer.) These schemes can be combined, as in IntToDoubleFunction.
  • If there are specialization prefixes for all arguments, the arity prefix may be left out (as in ObjIntConsumer).

上面这一大段内容,翻译跟总结下大概的意思就是 java.util.function 这个包下的函数式接口遵循一种可扩展的命名规则:

  • 基本函数形态 包括下面几种基本的函数类型:

    • Function:从 T 类型到 R 类型 的一元函数。

    • Consumer:接收一个 T 类型的参数但不返回任何结果(void)的一元函数。

    • Predicate:从 T 类型 到布尔值的一元函数。

    • Supplier: 不接收任何参数,返回类型 R 的函数(没有参数的函数)。

  • 函数的自然元数(arity) 就是每种函数形态根据其常见用途具有自然的元数。基础形态可以通过前缀来修改元数,例如:

    • BiFunction:一个二元函数,接收 T 和 U 类型的参数,返回类型R。
  • 扩展的函数形态:在基本函数接口的基础上扩展的函数接口,比如:

    • UnaryOperator:扩展的是 Function 这个接口, 输入输出的类型都一样。
    • BinaryOperator:扩展的是 BiFunction 这个接口, 输入输出的类型都一样。
  • 函数式接口的类型参数可以变成原始类型,通过在类型前面加前缀来实现。返回类型跟参数也可以同时是泛型,使用 ToXxx 前缀,比如 ToIntFunction 。如果是专门针对某个类型的参数,通常是从左到右进行命名,如 DoubleConsumerObjIntConsumer

4. 参考链接