2.1 Consumer接口
问题
用户希望编写实现 java.util.function.Consumer 包的 lambda 表达式。
方案
使用 lambda 表达式或方法引用来实现 void accept(T t) 方法。
讨论
例 2-1 列出了 Consumer 接口定义的方法,其单一抽象方法为 void accept(T t)。
例 2-1
Consumer接口定义的方法
- void accept(T t) ➊
- default Consumer<T> andThen(Consumer<? super T> after) ➋
❶ 单一抽象方法
❷ 用于复合操作的默认方法
accept 方法传入一个泛型参数并返回 void。在所有传入 Consumer 作为参数的方法中,最常见的是 java.util.Iterable 接口的默认 forEach 方法,如例 2-2 所示。
例 2-2
Iterable接口定义的forEach方法
- default void forEach(Consumer<? super T> action) ➊
➊ 将可迭代集合(iterable collection)中的所有元素传递给 Consumer 参数
如例 2-3 所示,所有线性集合通过对集合中每个元素执行给定的操作来实现 Iterable 接口。
例 2-3 打印集合中的元素
- List<String> strings = Arrays.asList("this", "is", "a", "list", "of", "strings");
- strings.forEach(new Consumer<String>() { ➊
- @Override
- public void accept(String s) {
- System.out.println(s);
- }
- });
- strings.forEach(s -> System.out.println(s)); ➋
- strings.forEach(System.out::println); ➌
❶ 匿名内部类实现
❷ lambda 表达式
❸ 方法引用
在本例中,由于 accept 方法只传入一个参数且不返回任何值,其签名与 lambda 表达式相符。通过 System.out 访问 PrintStream 类的 println 方法,它与 Consumer 相互兼容。因此,最后两条语句都可以作为 Consumer 参数的目标。
如表 2-1 所示,java.util.function 包还定义了三种 Consumer 的基本变体,以及一种双参数形式。
表2-1:其他Consumer接口
| 接口 | 单一抽象方法 |
|---|---|
IntConsumer
|
void accept(int x)
|
DoubleConsumer
|
void accept(double x)
|
LongConsumer
|
void accept(long x)
|
BiConsumer
|
void accept(T t, U u)
|
![]()
Consumer接口期望执行带有副作用的操作(即它可能会改变输入参数的内部状态),参见范例 2.3。
BiConsumer 接口的 accept 方法传入两个泛型参数,这两个泛型参数应为不同的类型。java.util.function 包定义了 BiConsumer 接口的三种变体,每种变体的第二个参数为基本数据类型。以 ObjIntConsumer 接口为例,其 accept 方法传入两个参数,分别为泛型参数和 int 参数。ObjLongConsumer 和 ObjDoubleConsumer 接口的定义与 ObjIntConsumer 类似。
标准库还支持 Consumer 接口的一些其他用法。
Optional.ifPresent(Consumer consumer)
如果值存在,则调用指定的 consumer;否则不进行任何操作。
Stream.forEach(Consumer action)
对流的每个元素执行操作 1。Stream.forEachOrdered 方法与之类似,它根据元素的出现顺序(encounter order)访问元素。
1这项操作十分常见,因此 Iterable 接口也直接定义了 forEach 方法。当源元素不是来自集合或需要创建并行流时,Stream.forEach 方法就很有用。
Stream.peek(Consumer action)
首先执行给定操作,再返回一个与现有流包含相同元素的流。peek 方法在调试中极为有用(参见范例 3.5)。
另见
Consumer 接口定义的 andThen 方法用于函数复合(function composition),详细讨论参见范例 5.8。有关 Stream.peek 方法的讨论参见范例 3.5。
