2.1 Consumer接口

问题

用户希望编写实现 java.util.function.Consumer 包的 lambda 表达式。

方案

使用 lambda 表达式或方法引用来实现 void accept(T t) 方法。

讨论

例 2-1 列出了 Consumer 接口定义的方法,其单一抽象方法为 void accept(T t)

例 2-1 Consumer 接口定义的方法

  1. void accept(T t)
  2. default Consumer<T> andThen(Consumer<? super T> after)

❶ 单一抽象方法

❷ 用于复合操作的默认方法

accept 方法传入一个泛型参数并返回 void。在所有传入 Consumer 作为参数的方法中,最常见的是 java.util.Iterable 接口的默认 forEach 方法,如例 2-2 所示。

例 2-2 Iterable 接口定义的 forEach 方法

  1. default void forEach(Consumer<? super T> action)

➊ 将可迭代集合(iterable collection)中的所有元素传递给 Consumer 参数

如例 2-3 所示,所有线性集合通过对集合中每个元素执行给定的操作来实现 Iterable 接口。

例 2-3 打印集合中的元素

  1. List<String> strings = Arrays.asList("this", "is", "a", "list", "of", "strings");
  2.  
  3. strings.forEach(new Consumer<String>() {
  4. @Override
  5. public void accept(String s) {
  6. System.out.println(s);
  7. }
  8. });
  9.  
  10. strings.forEach(s -> System.out.println(s));
  11. 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)

2.1 Consumer接口 - 图1 Consumer 接口期望执行带有副作用的操作(即它可能会改变输入参数的内部状态),参见范例 2.3。

BiConsumer 接口的 accept 方法传入两个泛型参数,这两个泛型参数应为不同的类型。java.util.function 包定义了 BiConsumer 接口的三种变体,每种变体的第二个参数为基本数据类型。以 ObjIntConsumer 接口为例,其 accept 方法传入两个参数,分别为泛型参数和 int 参数。ObjLongConsumerObjDoubleConsumer 接口的定义与 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。