关键在于谁先接收原始输入:f.andthen(g)等价于g(f(x)),f先执行;f.compose(g)等价于f(g(x)),g先执行,且g的输出类型必须匹配f的输入类型。

理解 Function 的 compose 和 andThen 关键不在“正向/反向”这种模糊说法,而在于**谁先拿到原始输入**——这直接决定类型是否能对上、逻辑是否符合业务意图。
执行顺序本质:谁吃第一口?
给定两个函数:f: T → R(比如 String → Integer)g: R → V(比如 Integer → Boolean)
-
f.andThen(g):f 先吃输入(T),产出 R;g 接着吃这个 R,产出 V。等价于数学表达式
g(f(x))。 -
f.compose(g):g 先吃输入(必须是 V 类型?错!是
V → T才能喂给 f),但实际签名是Function<v t></v>→ f。所以真正执行的是f(g(x)),即 g 先执行,输出必须是 f 的输入类型 T。
常见误判:以为 f.compose(g) 是 “g 在后面”,其实它要求 g 的输出类型 = f 的输入类型 —— g 必须能“生成 f 要的东西”。
类型不匹配是第一个陷阱
编译报错 Cannot resolve method 'apply' 多半是因为泛型断链。例如:
Function<string integer> parse = s -> Integer.parseInt(s);</string>Function<long string> toString = l -> l.toString();</long>-
parse.compose(toString)❌ 编译失败:toString 输出String,但 parse 需要String输入 —— 看似对?错!compose要求toString的输出类型必须等于parse的输入类型,而toString输入是Long,输出是String,类型是Function<long string></long>;parse是Function<string integer></string>,所以parse.compose(toString)实际要求toString的输出String匹配parse的输入String✅,但toString的输入是Long,意味着你得传Long给整个组合函数 —— 这没问题。真正陷阱常出在写反了方向:toString.compose(parse)才会爆:parse 输出Integer,toString 输入是Long,Integer ≠ Long。
记住:看 .compose(before) 就盯住 before 的输出类型,它必须和当前函数的输入类型一致。
业务逻辑顺序错位是第二个陷阱
比如权限校验应前置,但写成:
-
userService::findById.andThen(authCheck)→ 先查用户,再校验,已暴露数据。 - 正确应为:
authCheck.andThen(userService::findById),或更自然地:userService::findById.compose(authCheck)(authCheck 先执行,通过后才调用 findById)。
compose 更适合“加一道前置守门员”,andThen 更适合“加工流水线末端追加一步”。选哪个,取决于你当前写的函数在流程里是“主力”还是“守门员”。
阅读与调试时的真实感受
链越长,越容易晕:
-
f.andThen(g).andThen(h):从左到右读,执行也是左→右 → 直观。 -
h.compose(g).compose(f):从右往左执行(f 最先),但代码是从左写到右 → 容易误读为 h 最先。
建议:对主干流程用 andThen(如清洗→转换→落库),对可插拔前置步骤(如日志、校验、缓存)用 compose 封装,并单独命名,比如 withAuthCheck(userService::findById),内部用 authCheck.compose(...),对外隐藏组合细节。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











