README · 函数式编程
Java 函数式编程
函数式编程不是“把代码改成 Lambda”这么简单。它更重要的思想是:把计算描述为可组合的函数,尽量减少共享可变状态,让数据从输入经过一系列转换得到输出。 Java 不是纯函数式语言,但从 Java 8 开始,可以借助 Lambda、函数式接口、方法引用、Stream 与 Optional 写出更声明式的代码。
代码块收起展开
List<String> names = users.stream()
.filter(User::active)
.map(User::name)
.map(String::trim)
.filter(name -> !name.isEmpty())
.distinct()
.sorted()
.toList();这段代码描述的是“筛选 → 映射 → 清洗 → 去重 → 排序 → 收集”,而不是手动维护循环变量和临时集合。
学习路线
- 函数式思想、Lambda 与函数式接口
- 方法引用
- 闭包、柯里化与高阶函数
- Stream 模型与创建方式
- Stream 中间操作、终止操作与收集器
- 查找、匹配与短路操作
- Optional 与空值处理
- 手写 SimpleStream 理解原理
核心概念速查
| 概念 | 关键问题 | Java 中的载体 |
|---|---|---|
| 纯函数 | 相同输入是否总得到相同输出,且不修改外部状态 | 无副作用的方法、不可变对象 |
| 一等函数 | 函数能否像数据一样传入或返回 | Lambda、方法引用、函数式接口 |
| 高阶函数 | 是否接收函数或返回函数 | map、filter、reduce、compose |
| 闭包 | 函数能否捕获定义环境中的变量 | Lambda 捕获 effectively final 变量 |
| 惰性计算 | 是否等到真正需要结果才执行 | Stream 中间操作 |
| 归约 | 如何把多个元素合成为一个结果 | reduce、collect |
一条完整的数据管道
代码块收起展开
record Order(String user, int amount, boolean paid) {}
Map<String, Integer> paidAmountByUser = orders.stream()
.filter(Order::paid)
.collect(Collectors.groupingBy(
Order::user,
Collectors.summingInt(Order::amount)
));阅读顺序是:数据源 orders → 保留已支付订单 → 按用户分组 → 对金额求和 → 得到结果。中间操作负责描述转换,终止操作 collect 才真正触发执行。
使用边界
- 需要清晰的数据转换管道时,优先使用 Stream。
- 简单循环、包含大量
break/continue、复杂异常处理或强顺序副作用时,普通循环可能更直观。 - 不要为了“函数式”而把一段逻辑压成一行;可读性比链式调用长度更重要。
- Stream 默认只能消费一次;不要在
forEach中修改流外的共享集合。 parallelStream()不是免费加速,必须先确认数据量、任务性质、线程安全和顺序要求。
自测清单
- 能区分
Function、Predicate、Consumer、Supplier。 - 能解释闭包为什么只能捕获 final 或 effectively final 的局部变量。
- 能判断
map与flatMap、reduce与collect的使用场景。 - 能解释
orElse与orElseGet的求值差异。 - 能写出分组、分区、去重、排序、查找和归约管道。
- 能指出副作用、重复消费 Stream、错误使用并行流等常见问题。