2.4 Function接口
问题
用户希望实现 java.util.function.Function 接口,以便将输入参数转换为输出值。
方案
提供一个实现 R apply(T t) 方法的 lambda 表达式。
讨论
Function 接口包含的单一抽象方法为 apply,它可以将 T 类型的泛型输入参数转换为 R 类型的泛型输出值。例 2-12 列出了 Function 接口定义的所有方法。
例 2-12
Function接口定义的方法
- default <V> Function<T,V> andThen(Function<? super R,? extends V> after)
- R apply(T t)
- default <V> Function<V,R> compose(Function<? super V,? extends T> before)
- static <T> Function<T,T> identity()
Function 最常见的用法是作为 Stream.map 方法的一个参数。例如,为了将 string 转换为整数,可以在每个实例上调用 length 方法,如例 2-13 所示。
例 2-13 将字符串映射到它们的长度
- List<String> names = Arrays.asList("Mal", "Wash", "Kaylee", "Inara",
- "Zoë", "Jayne", "Simon", "River", "Shepherd Book");
- List<Integer> nameLengths = names.stream()
- .map(new Function<String, Integer>() { ➊
- @Override
- public Integer apply(String s) {
- return s.length();
- }
- })
- .collect(Collectors.toList());
- nameLengths = names.stream()
- .map(s -> s.length()) ➋
- .collect(Collectors.toList());
- nameLengths = names.stream()
- .map(String::length) ➌
- .collect(Collectors.toList());
- System.out.printf("nameLengths = %s%n", nameLengths);
- // 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,该方法的参数可能是 ToIntFunction。Stream.mapToInt 方法传入 ToIntFunction 作为参数,mapToDouble 和 mapToLong 方法与之类似。
mapToInt、mapToDouble 与 mapToLong 的返回类型分别为 IntStream、DoubleStream 和 LongStream。
那么,如果输入参数和返回类型相同呢? java.util.function 包为此定义了 UnaryOperator 接口,以及相应的 IntUnaryOperator、DoubleUnaryOperator 和 LongUnaryOperator 接口,三者的输入和输出参数分别为 int、double 和 long。UnaryOperator 的一种应用是 StringBuilder 的 reverse 方法,因为输入类型和输出类型均为字符串。
BiFunction 接口定义了两个泛型输入类型和一个泛型输出类型,三者应为不同的类型。如果三者相同,可以使用 java.util.function 包定义的 BinaryOperator 接口。Math.max 就是一种二元运算符,其输入和输出为 int、double、float 或 long 型数据。相应地,java.util.function 包也定义了 IntBinaryOperator、DoubleBinaryOperator 与 LongBinaryOperator 接口。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 工具类中。
有关 andThen 和 compose 方法的讨论请参见范例 5.8。identity 方法实际上是一种简单的 lambda 表达式(e -> e),范例 4.3 将介绍其中一种应用。
另见
有关 Function.andThen 和 Function.compose 方法的应用请参见范例 5.8,有关 Function.identity 方法的应用请参见范例 4.3,有关下游收集器的讨论请参见范例 4.6,有关 computeIfAbsent 方法的讨论请参见范例 5.4,有关二元运算符的讨论请参见范例 3.3。
