handle()返回新类型CompletableFuture,统一处理成功与异常并转为R;whenComplete()返回原类型CompletableFuture,仅观测副作用不改变结果;exceptionally()返回同类型CompletableFuture,仅在异常时提供替代值。

CompletableFuture 的异常处理方法中,handle()、whenComplete() 和 exceptionally() 表面相似,但返回类型差异直接决定了它们在链式调用中的行为边界——这不是风格选择问题,而是类型契约决定的流程走向。
handle():返回新类型的 CompletionStage
handle 接收 BiFunction,返回 CompletableFuture。它强制你提供一个统一的转换逻辑,无论上游成功(T)还是失败(Throwable),都必须产出一个 R 类型结果。
- 若上游成功,传入 (result, null),你返回 R;
- 若上游失败,传入 (null, exception),你也得返回 R —— 这就是“消费异常”并转为正常值的关键;
- 返回的新 CompletableFuture 类型已变为 R,后续可接 thenApply
、thenCompose 等,链路不中断。
whenComplete():返回原类型的 CompletionStage
whenComplete 接收 BiConsumer,返回 CompletableFuture —— 类型不变,仍是原始泛型 T。
- 它只做“观测”或“副作用”,比如打日志、关连接、发告警;
- 无法修改结果,也不能把异常“掩盖”成有效值;
- 如果上游异常未被 earlier 阶段(如 exceptionally)拦截,whenComplete 执行后仍会保留该异常,下游调用 get() 时抛出 CompletionException。
exceptionally():返回同类型 CompletionStage(仅异常分支)
exceptionally 接收 Function,返回 CompletableFuture —— 类型与上游一致,但只在异常路径触发。
- 上游成功时,此方法完全跳过,不影响结果;
- 上游失败时,你用异常生成一个 T 类型的兜底值(如空列表、默认字符串、缓存快照);
- 它不接收正常结果,也不允许你访问 result;它的契约就是“异常 → 替代值”,干净利落。
三者不是互斥选项,而是分层协作工具:exceptionally 负责兜底保链路,handle 负责统一归一化,whenComplete 负责可观测性。选错返回类型,轻则编译报错,重则异常静默丢失或类型不匹配中断链式流。










