Java 8的Function接口中,andThen正向组合(先自己后他人),compose反向组合(先他人后自己),二者等价但视角不同,适用于构建清晰的数据流水线。

Java 8 的 Function 接口提供了 andThen 和 compose 两个默认方法,它们让函数组合变得直观、可读且符合函数式编程习惯。掌握它们,就能把零散的转换逻辑串成一条清晰的数据流水线。
andThen:正向执行,先自己再别人
andThen 表示“我处理完,把结果交给下一个函数”。它的签名是:<V> Function<T, V> andThen(Function<R, V> after)
注意:当前函数输出类型 R 必须和 after 的输入类型一致。
适合从原始数据出发,一步步加工的场景,比如:
- 字符串 → 去空格 → 转大写 → 取前5字符
- 订单对象 → 提取用户ID → 查询用户 → 获取用户等级
示例:
Function<String, String> trim = s -> s.trim();Function<String, String> upper = s -> s.toUpperCase();
Function<String, String> take5 = s -> s.substring(0, Math.min(5, s.length()));
Function<String, String> pipeline = trim.andThen(upper).andThen(take5);
System.out.println(pipeline.apply(" hello world ")); // 输出:HELLO
compose:反向拼接,先别人再自己
compose 表示“我等着别人处理完的结果,再接手”。签名是:<V> Function<V, R> compose(Function<V, T> before)
即:先执行 before(输入 V,输出 T),再把结果喂给当前函数(输入 T,输出 R)。
它更贴近“前置依赖”的思维,适合封装可复用的中间步骤,或需要先做预处理再进入主逻辑时使用。
示例(等价于上面的链):
Function<String, String> pipeline2 = take5.compose(upper).compose(trim);System.out.println(pipeline2.apply(" hello world ")); // 同样输出:HELLO
虽然结果一样,但阅读顺序是从右往左——trim 最先执行,take5 最后执行。
andThen 与 compose 的本质区别不是方向,而是调用视角
两者不是“正向 vs 反向”的对立,而是站在不同函数角度描述同一组合关系:
-
f.andThen(g)等价于g.compose(f) - 都表示:输入 → f → g → 输出
- 选哪个,取决于你当前正在写的函数是“主线”还是“前置环节”
比如封装一个通用校验器:
// 想让校验逻辑作为前置步骤插入到已有流程中Function<String, Boolean> notEmpty = s -> !s.trim().isEmpty();
Function<String, Integer> parseLen = s -> s.length();
// 把校验塞到 parseLen 前面:
Function<String, Integer> safeLen = parseLen.compose(notEmpty.andThen(s -> s)); // 这样不行,类型不匹配
更合理的做法是统一用对象包装,或改用 Predicate + Function 组合——说明:别硬套,类型对齐才是关键。
实战小技巧:用 andThen 构建可读管道,用 compose 复用预处理
日常开发中建议:
- 主数据流用
andThen:从源数据开始,一路往后写,符合阅读直觉 - 通用工具函数(如格式化、安全检查、默认值兜底)优先设计为
Function,并考虑是否提供compose版本便于嵌入 - 避免多层嵌套
compose(compose(...)),可提取中间变量提升可读性 - 注意 null 处理:这些方法本身不处理 null,需在各环节自行 guard,或用
Optional包装
例如加一层空值保护:
Function<String, String> safeTrim = s -> s == null ? "" : s.trim();Function<String, String> safeUpper = s -> s == null ? "" : s.toUpperCase();
Function<String, String> robustPipeline = safeTrim.andThen(safeUpper).andThen(take5);

















