当前位置: 首页 > news >正文

理解Consumer<T>

为什么要创造Consumer,创造出它是为了什么

Java 8 之前若想把“一段代码”当参数传,只能传 对象;而对象必须先 写类、写方法、new 实例,步骤又重又死板。
于是官方总结:80 % 的回调场景其实只符合“给我一个 T,我拿去干点事,什么都不用还” 这一句话。
干脆把这句话 固化成接口,让编译器 直接把 Lambda 当成实例——这就是 Consumer 被创造出来的唯一目的:
让“行为”像数据一样,一键传递、组合、复用,而不再被类/对象样板代码绑架。

下面把 Consumer<T> 从“接口定义 → 使用动机 → 实现方式 → 在 JDK 中的落地 → 自己手写一套类似机制”完整拆开讲一遍,力求“一页纸看懂”。


一、接口定义(java.util.function.Consumer)

@FunctionalInterface          // 只能有一个抽象方法
public interface Consumer<T> {/*** 对给定值做“副作用”操作,无返回。* 所谓副作用:打印、写库、改外部变量、发网络请求……*/void accept(T t);/*** 默认组合方法:先执行当前 Consumer,再执行 after。* 返回一个“复合 Consumer”,便于链式拼装。*/default Consumer<T> andThen(Consumer<? super T> after) {Objects.requireNonNull(after);return (T t) -> { accept(t); after.accept(t); };}
}

二、设计动机(为什么多此一举搞个接口?)

  1. 把“行为”变成数据
    以前写代码只能把“算法”写死在方法体内;现在可以把算法当成参数传来传去——行为参数化

  2. 支持高阶函数
    方法签名一旦写成
    void xxx(Consumer<T> c)
    就意味着“xxx 内部会在某个时机帮你执行 c”,从而完成控制反转(框架调用业务代码)。

  3. 配合 Lambda 实现函数式编程
    单方法接口 + @FunctionalInterface 让 Lambda 表达式可以自动匹配成接口实例,语法糖极致简洁。


三、实现方式(三种写法)

List<String> list = List.of("a", "b", "c");// 1. 匿名内部类(最原始)
list.forEach(new Consumer<String>() {@Overridepublic void accept(String s) {System.out.println(s);}
});// 2. Lambda(最常用)
list.forEach(s -> System.out.println(s));// 3. 方法引用(再简一步)
list.forEach(System.out::println);

编译后三种方式都会生成一个实现了 Consumer 接口的对象,运行时 JVM 会调用它的 accept 方法。


四、在 JDK 中的“落地”位置(部分)

所在类/接口使用 Consumer 的方法说明
Iterable<T>forEach(Consumer<? super T>)遍历元素
Stream<T>forEach(Consumer<T>) / peek(Consumer<T>)终端/中间操作
Optional<T>ifPresent(Consumer<? super T>)值存在才消费
Map<K,V>forEach(BiConsumer<K,V>)BiConsumer 是 Consumer 的“两参”变体
CompletableFuture<T>thenAccept(Consumer<T>)异步回调
ArrayList(重载)forEach(Consumer<? super E>)默认方法实现

五、自己动手写一套“类似机制”

假设没有 Consumer,我们照样可以模拟“行为参数化”:

// 1. 自定义函数式接口
@FunctionalInterface
interface MyHandler<T> {void handle(T t);
}// 2. 工具类里提供“遍历 + 回调”
class Lists {public static <T> void each(Iterable<T> src, MyHandler<T> h) {for (T t : src) {h.handle(t);      // 框架调用业务代码}}
}// 3. 使用
Lists.each(Arrays.asList(1,2,3), x -> System.out.println(x * 10));

输出:
10
20
30

这段代码跟 list.forEach(x -> System.out.println(x * 10)) 本质一模一样,区别只是接口名不同。


六、常见踩坑点

  1. 在 Lambda 里修改外部局部变量必须声明为 final 或“事实 final”。
    原因:Lambda 实例可能延迟执行,Java 要保证捕获的变量安全。

  2. Consumer 只负责“副作用”,不要在里面有返回逻辑
    如果想“把 T 转换成 R”请用 Function<T,R>

  3. 链式 andThen 的顺序:
    c1.andThen(c2)c1c2,异常会在最近的一次 accept 中抛出。


七、一张图总结(文字版)

外部代码 ──Lambda──▶ Consumer 实例│                │▼                ▼框架方法        accept(t) 被执行
(forEach/peek)

→ 把“要做什么”交给 Consumer
→ 框架决定“什么时候做、做多少次
→ 于是算法流程彻底解耦。


记住一句话:
Consumer<T> 就是 Java 8 对“单参数 + 无返回 + 副作用”这种最常见回调语义的官方封装,让“行为”能够像数据一样被传递、组合、复用。

http://www.dtcms.com/a/438390.html

相关文章:

  • Linux:了解Socket编程
  • UE4报错:无法编译项目
  • Cobalt Strike
  • 牛客算法基础noob61 字符串操作
  • 快速制作简单的网站网站建设 中山
  • 网络爬虫技术规范与应用指南系列(xc—1)
  • CSS Border(边框)
  • Photoshop - Photoshop 工具库
  • 2.1.2 扩展知识:AI 语音通话
  • 动态目标检测与跟踪:基于卡尔曼滤波的门限关联与可视化全流程
  • Codeforces Round 863 A. Insert Digit (1811)
  • 企业网站建立要做的准备更新电脑版wordpress
  • 阳江做网站公司南昌seo优化公司
  • 光子桥揭开可调激光PIC的面纱
  • C++——类和对象3
  • Linux第二十四讲:多路转接epoll
  • 专业网站定制流程网站建设公司是怎么找客户
  • Unity学习之C#的反射机制
  • Python环境管理工具全景对比:Virtualenv, Pipenv, Poetry 与 Conda
  • 郑州企业如何建网站wordpress微信付费
  • 微信小程序入门学习教程,从入门到精通,微信小程序开发进阶(7)
  • 数据结构和算法篇--带哨兵节点的双链表
  • 6黄页网站建设做网站怎么去工信部缴费
  • 三支一扶面试资料
  • pytorch 52 基于SVD从全量训练模型中提取lora模型
  • Process Monitor 学习笔记(5.7):长时间运行追踪与日志体积控制
  • 深入解析需求变更:从本质认知到实践指南
  • 商城网站建设的步骤网络设计教程
  • Day 30 - 错误、异常与 JSON 数据 - Python学习笔记
  • 吴恩达机器学习笔记(10)—支持向量机