README · 函数式编程

Java 函数式编程

函数式编程不是“把代码改成 Lambda”这么简单。它更重要的思想是:把计算描述为可组合的函数,尽量减少共享可变状态,让数据从输入经过一系列转换得到输出。 Java 不是纯函数式语言,但从 Java 8 开始,可以借助 Lambda、函数式接口、方法引用、StreamOptional 写出更声明式的代码。

代码块JAVA · 8 行收起展开
List<String> names = users.stream()
        .filter(User::active)
        .map(User::name)
        .map(String::trim)
        .filter(name -> !name.isEmpty())
        .distinct()
        .sorted()
        .toList();

这段代码描述的是“筛选 → 映射 → 清洗 → 去重 → 排序 → 收集”,而不是手动维护循环变量和临时集合。

学习路线

  1. 函数式思想、Lambda 与函数式接口
  2. 方法引用
  3. 闭包、柯里化与高阶函数
  4. Stream 模型与创建方式
  5. Stream 中间操作、终止操作与收集器
  6. 查找、匹配与短路操作
  7. Optional 与空值处理
  8. 手写 SimpleStream 理解原理

核心概念速查

概念关键问题Java 中的载体
纯函数相同输入是否总得到相同输出,且不修改外部状态无副作用的方法、不可变对象
一等函数函数能否像数据一样传入或返回Lambda、方法引用、函数式接口
高阶函数是否接收函数或返回函数mapfilterreducecompose
闭包函数能否捕获定义环境中的变量Lambda 捕获 effectively final 变量
惰性计算是否等到真正需要结果才执行Stream 中间操作
归约如何把多个元素合成为一个结果reducecollect

一条完整的数据管道

代码块JAVA · 8 行收起展开
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() 不是免费加速,必须先确认数据量、任务性质、线程安全和顺序要求。

自测清单

  • 能区分 FunctionPredicateConsumerSupplier
  • 能解释闭包为什么只能捕获 final 或 effectively final 的局部变量。
  • 能判断 mapflatMapreducecollect 的使用场景。
  • 能解释 orElseorElseGet 的求值差异。
  • 能写出分组、分区、去重、排序、查找和归约管道。
  • 能指出副作用、重复消费 Stream、错误使用并行流等常见问题。

延伸阅读