在Java的函数式编程体系中,Consumer接口是专门用于处理无返回值逻辑动作的核心接口,它接收一个输入参数但不返回任何结果,非常适合用来封装那些只需要执行操作、不需要产出结果的业务场景。

Consumer接口的基本定义
Consumer接口位于java.util.function包下,是一个函数式接口,它只包含一个抽象方法accept,该方法接收一个泛型参数T,执行对应的逻辑但不返回任何值。其源码定义简化后如下:
@FunctionalInterface
public interface Consumer<T> {
// 接收一个参数T,执行对应逻辑,无返回值
void accept(T t);
// 默认方法,用于组合多个Consumer逻辑
default Consumer<T> andThen(Consumer<? super T> after) {
if (after == null) {
throw new NullPointerException();
}
return (T t) -> {
accept(t);
after.accept(t);
};
}
}
基础使用方式
因为Consumer是函数式接口,所以可以直接配合Lambda表达式使用,不需要额外编写实现类。最常见的场景就是对集合中的元素执行统一操作,比如遍历集合并打印每个元素:
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerDemo {
public static void main(String[] args) {
List<String> nameList = Arrays.asList("张三", "李四", "王五");
// 定义Consumer逻辑:打印输入的字符串
Consumer<String> printConsumer = name -> System.out.println("当前姓名:" + name);
// 遍历集合,对每个元素应用Consumer逻辑
for (String name : nameList) {
printConsumer.accept(name);
}
}
}
上面的代码中,printConsumer封装了打印姓名的逻辑,调用accept方法时传入对应的参数即可执行操作,不需要返回任何结果。
结合集合forEach方法使用
Java的集合框架中,Iterable接口提供了forEach方法,该方法接收的参数就是Consumer接口,因此可以直接传入Lambda表达式作为Consumer的实现,简化遍历操作的代码:
import java.util.Arrays;
import java.util.List;
public class ForEachDemo {
public static void main(String[] args) {
List<Integer> scoreList = Arrays.asList(85, 92, 78, 90);
// forEach接收Consumer,直接编写无返回值的逻辑
scoreList.forEach(score -> {
if (score >= 90) {
System.out.println("优秀成绩:" + score);
}
});
}
}
andThen方法组合多个逻辑
Consumer接口的andThen默认方法可以实现多个无返回值逻辑的串联,先执行当前Consumer的accept方法,再执行传入的after Consumer的accept方法。比如我们需要先打印用户信息,再更新用户的最后操作时间:
import java.util.function.Consumer;
class User {
private String name;
private String lastOperateTime;
public User(String name) {
this.name = name;
}
public void setName(String name) {
this.name = name;
}
public void setLastOperateTime(String lastOperateTime) {
this.lastOperateTime = lastOperateTime;
}
public String getName() {
return name;
}
}
public class AndThenDemo {
public static void main(String[] args) {
User user = new User("赵六");
// 第一个逻辑:打印用户信息
Consumer<User> printUser = u -> System.out.println("用户姓名:" + u.getName());
// 第二个逻辑:更新最后操作时间
Consumer<User> updateTime = u -> {
u.setLastOperateTime("2024-05-20 14:30:00");
System.out.println("已更新用户操作时间");
};
// 组合两个逻辑,先执行printUser,再执行updateTime
Consumer<User> combinedConsumer = printUser.andThen(updateTime);
combinedConsumer.accept(user);
}
}
Consumer的变体形式
除了基础的Consumer接口,Java还提供了几个基本类型的Consumer变体,避免基本类型的装箱拆箱开销:
IntConsumer:接收int类型参数,无返回值LongConsumer:接收long类型参数,无返回值DoubleConsumer:接收double类型参数,无返回值
使用示例:
import java.util.function.IntConsumer;
public class PrimitiveConsumerDemo {
public static void main(String[] args) {
IntConsumer squareConsumer = num -> System.out.println(num + "的平方是:" + (num * num));
squareConsumer.accept(5);
}
}
和其他无返回值接口的对比
和Consumer类似的接口还有Runnable,二者的区别是Runnable不接收任何参数,而Consumer接收一个输入参数。如果逻辑动作不需要依赖外部传入的参数,用Runnable即可;如果需要基于传入的参数执行操作,就选择Consumer。
| 接口 | 参数数量 | 返回值 | 适用场景 |
|---|---|---|---|
| Consumer<T> | 1个泛型参数T | 无 | 基于传入的参数执行无返回值操作 |
| Runnable | 0个 | 无 | 执行无参数无返回值的操作 |
使用注意事项
- Consumer的
accept方法不应该抛出受检异常,如果需要处理异常,建议在Lambda表达式内部做好异常捕获。 - 不要在Consumer的逻辑中修改传入的参数引用指向的对象,除非是明确需要修改对象内部属性的场景,避免产生不可预期的副作用。
- 当逻辑比较简单时,可以直接把Lambda表达式传入需要Consumer参数的方法,不需要单独定义Consumer变量,减少冗余代码。