带你学会 Java 的 Supplier 和 Consumer 接口的使用
·
理解
Supplier与Consumer接口的本质与实战意义,将帮你构建更灵活、更优雅的数据处理模型。
一、背景
1. 概述
Java 8 引入的函数式接口为编程范式带来了革命性突破,其中 Supplier 与 Consumer 占据核心地位。二者分别封装了“数据供给”与“数据消费”的逻辑,极大增强了代码的解耦性、可读性和可维护性。
2. 概念类比
可将 Supplier 和 Consumer 类比为“数据处理流水线”的两个节点:
| 接口 | 类比角色 | 描述 |
|---|---|---|
| Supplier | 原料输出端 | 无需输入,提供数据 |
| Consumer | 加工车间 | 接收输入,执行处理 |
这种“生产者-消费者”模型在计算机科学中广泛存在。
二、Supplier 接口
1. 接口定义
@FunctionalInterface
public interface Supplier<T> {
T get();
}
-
无输入,返回一个
T类型的结果。 -
常用于延迟计算、懒加载、数据源抽象等场景。
2. 典型应用场景
✅ 场景一:延迟初始化
private HeavyObject heavyObject;
private final Supplier<HeavyObject> supplier = () -> new HeavyObject();
public HeavyObject getHeavyObject() {
if (heavyObject == null) {
heavyObject = supplier.get();
}
return heavyObject;
}
✅ 场景二:随机数据生成
Supplier<Integer> RANDOM_SUPPLIER = () -> new Random().nextInt(100);
Stream.generate(RANDOM_SUPPLIER).limit(5).forEach(System.out::println);
✅ 场景三:策略化数据提供
DataProvider provider = new DataProvider(() -> "Data from Cache");
System.out.println(provider.fetchData()); // 输出:Data from Cache
三、Consumer 接口
1. 接口定义
@FunctionalInterface
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
return (T t) -> { accept(t); after.accept(t); };
}
}
-
接收一个输入,无返回值。
-
常用于日志打印、状态变更、消息推送等“副作用”处理。
2. 典型应用场景
✅ 场景一:打印或格式化输出
Consumer<String> print = System.out::println;
Consumer<String> format = s -> System.out.println("Formatted: " + s.toUpperCase());
print.andThen(format).accept("hello");
✅ 场景二:集合批量处理
List<Integer> numbers = Arrays.asList(1, 2, 3);
numbers.forEach(n -> System.out.print(n + " ")); // 输出:1 2 3
✅ 场景三:对象属性修改
Consumer<User> nameUpdater = u -> u.setName("李四");
Consumer<User> ageUpdater = u -> u.setAge(u.getAge() + 5);
nameUpdater.andThen(ageUpdater).accept(user);
四、Supplier 与 Consumer 的对比分析
| 维度 | Supplier | Consumer |
|---|---|---|
| 核心职责 | 提供数据(生产者) | 处理数据(消费者) |
| 方法签名 | T get() |
void accept(T t) |
| 输入参数 | 无 | 有一个参数 |
| 返回值 | 有(T类型) | 无(void) |
| 应用场景 | 延迟加载、数据生成 | 打印、修改对象、数据落库 |
| 函数组合 | 不支持组合 | 支持 andThen() |
| 线程安全 | 通常线程安全 | 需注意副作用引起的线程安全问题 |
五、与匿名内部类的对比
| 实现方式 | Supplier 示例 | Consumer 示例 |
|---|---|---|
| 匿名类 | new Supplier<String>() { public String get() { return "x"; }} |
new Consumer<String>() { public void accept(String s) { }} |
| Lambda | () -> "x" |
s -> System.out.println(s) |
| 方法引用 | LocalDate::now |
System.out::println |
✅ 优势: 代码量减少 60%+,表达更直观,可组合、可复用。
六、在设计模式中的应用
1. 策略模式
new PriceCalculator(() -> 100.0, price -> System.out.println("VIP价: " + price * 0.8)).calculate();
2. 观察者模式
EventPublisher<String> publisher = new EventPublisher<>();
publisher.register(e -> System.out.println("Log: " + e));
publisher.register(e -> { if (e.contains("error")) System.out.println("Alert!"); });
3. 适配器模式
public class LegacyAdapter implements Supplier<String> {
private LegacyService legacyService;
public String get() { return legacyService.fetchData(); }
}
七、实际业务场景应用
✅ 场景:电商订单处理
Supplier<String> ORDER_ID_SUPPLIER = () -> "ORD-" + System.currentTimeMillis();
Consumer<Order> VALIDATE = order -> {
if (order.getProducts().isEmpty()) throw new IllegalArgumentException("不能为空");
};
Consumer<Order> SAVE = order -> System.out.println("保存订单: " + order.getOrderId());
Consumer<Order> NOTIFY = order -> System.out.println("通知用户: " + order.getOrderId());
// 组合处理流水线
Consumer<Order> orderPipeline = VALIDATE.andThen(SAVE).andThen(NOTIFY);
八、总结与延展
-
Supplier与Consumer是函数式接口体系中的代表,一个用于“数据提供”,一个用于“数据处理”。 -
它们在业务逻辑抽象、函数组合、设计模式实现等方面都有巨大价值。
📌 延伸建议
-
探索
Function<T, R>和Predicate<T>接口的组合使用 -
学习如何在 Spring、RxJava、Reactor 中应用函数式接口
-
结合 Stream API 构建复杂的数据转换流程
更多推荐


所有评论(0)