2.4 Function接口

问题

用户希望实现 java.util.function.Function 接口,以便将输入参数转换为输出值。

方案

提供一个实现 R apply(T t) 方法的 lambda 表达式。

讨论

Function 接口包含的单一抽象方法为 apply,它可以将 T 类型的泛型输入参数转换为 R 类型的泛型输出值。例 2-12 列出了 Function 接口定义的所有方法。

例 2-12 Function 接口定义的方法

  1. default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
  2. R apply(T t)
  3. default <V> Function<V,R> compose(Function<? super V,? extends T> before)
  4. static <T> Function<T,T> identity()

Function 最常见的用法是作为 Stream.map 方法的一个参数。例如,为了将 string 转换为整数,可以在每个实例上调用 length 方法,如例 2-13 所示。

例 2-13 将字符串映射到它们的长度

  1. List<String> names = Arrays.asList("Mal", "Wash", "Kaylee", "Inara",
  2. "Zoë", "Jayne", "Simon", "River", "Shepherd Book");
  3.  
  4. List<Integer> nameLengths = names.stream()
  5. .map(new Function<String, Integer>() {
  6. @Override
  7. public Integer apply(String s) {
  8. return s.length();
  9. }
  10. })
  11. .collect(Collectors.toList());
  12.  
  13. nameLengths = names.stream()
  14. .map(s -> s.length())
  15. .collect(Collectors.toList());
  16.  
  17. nameLengths = names.stream()
  18. .map(String::length)
  19. .collect(Collectors.toList());
  20.  
  21. System.out.printf("nameLengths = %s%n", nameLengths);
  22. // nameLengths == [3, 4, 6, 5, 3, 5, 5, 5, 13]

❶ 匿名内部类

❷ lambda 表达式

❸ 方法引用

表 2-3 列出了输入和输出泛型类型的所有基本变体。

表2-3:其他Function接口

接口 单一抽象方法
IntFunction R apply(int value)
DoubleFunction R apply(double value)
LongFunction R apply(long value)
ToIntFunction int applyAsInt(T value)
ToDoubleFunction double applyAsDouble(T value)
ToLongFunction long applyAsLong(T value)
DoubleToIntFunction int applyAsInt(double value)
DoubleToLongFunction long applyAsLong(double value)
IntToDoubleFunction double applyAsDouble(int value)
IntToLongFunction long applyAsLong(int value)
LongToDoubleFunction double applyAsDouble(long value)
LongToIntFunction int applyAsInt(long value)
BiFunction R apply(T t, U u)

在例 2-13 中,由于 map 方法返回的是基本数据类型 int,该方法的参数可能是 ToIntFunctionStream.mapToInt 方法传入 ToIntFunction 作为参数,mapToDoublemapToLong 方法与之类似。

mapToIntmapToDoublemapToLong 的返回类型分别为 IntStreamDoubleStreamLongStream

那么,如果输入参数和返回类型相同呢? java.util.function 包为此定义了 UnaryOperator 接口,以及相应的 IntUnaryOperatorDoubleUnaryOperatorLongUnaryOperator 接口,三者的输入和输出参数分别为 intdoublelongUnaryOperator 的一种应用是 StringBuilderreverse 方法,因为输入类型和输出类型均为字符串。

BiFunction 接口定义了两个泛型输入类型和一个泛型输出类型,三者应为不同的类型。如果三者相同,可以使用 java.util.function 包定义的 BinaryOperator 接口。Math.max 就是一种二元运算符,其输入和输出为 intdoublefloatlong 型数据。相应地,java.util.function 包也定义了 IntBinaryOperatorDoubleBinaryOperatorLongBinaryOperator 接口。3

3有关 Java 标准库中 BinaryOperator 用法的详细讨论,请参见范例 3.3。

表 2-4 列出了 BiFunction 接口的所有基本变体。

表2-4:其他BiFunction接口

接口 单一抽象方法
ToIntBiFunction int applyAsInt(T t, U u)
ToDoubleBiFunction double applyAsDouble(T t, U u)
ToLongBiFunction long applyAsLong(T t, U u)

尽管 Function 主要用于各种 Stream.map 方法,但这些方法也可能出现在其他上下文中,举例如下。

Map.computeIfAbsent(K key, Function mappingFunction)

  如果指定的键没有值,使用所提供的 Function 计算一个值并将其添加到 Map

Comparator.comparing(Function keyExtractor)

  comparing 方法生成一个 Comparator,使用给定 Function 生成的键对集合进行排序。相关讨论请参见范例 4.1。

Comparator.thenComparing(Function keyExtractor)

  thenComparing 是一种实例方法,也可以用于排序。如果集合的首次排序返回相同的值,则使用另一种机制进行排序。

此外,Function 还广泛用于分组和下游收集器(downstream collector)的 Collectors 工具类中。

有关 andThencompose 方法的讨论请参见范例 5.8。identity 方法实际上是一种简单的 lambda 表达式(e -> e),范例 4.3 将介绍其中一种应用。

另见

有关 Function.andThenFunction.compose 方法的应用请参见范例 5.8,有关 Function.identity 方法的应用请参见范例 4.3,有关下游收集器的讨论请参见范例 4.6,有关 computeIfAbsent 方法的讨论请参见范例 5.4,有关二元运算符的讨论请参见范例 3.3。