在 completionstage 的 applytoeither 方法中,直接传入一个函数式接口(如 function)是不合法的,因为该方法的签名要求参数是另一个 completionstage。要实现类似功能,你需要先通过 thenapply 或 thencompose 对两个阶段的结果进行转换,或者使用 completablefuture 提供的组合策略。核心做法是将两个异步任务包装成 completablefuture,然后利用 applytoeither 处理“谁先完成取谁”的逻辑,但注意 applytoeither 本身不接受函数作为第二个参数,它接受的是另一个 stage。

如何正确调用 applyToEither 并获取结果
applyToEither 接收另一个 CompletionStage 作为参数,返回一个新的 CompletionStage,该新阶段将在当前阶段或参量阶段任一完成时触发。若你想在其中一个完成时对结果应用函数,应链式调用 thenApply。例如,有两个异步任务 A 和 B,你希望 whichever completes first 的结果经过函数 f 处理后输出,代码结构为 a.applyToEither(b).thenApply(f)。这里 f 是一个 Function 类型的函数式接口实例。

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class ApplyToEitherDemo {
public static void main(String[] args) throws Exception {
CompletableFuture<string> futureA = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(100); } catch (InterruptedException e) {}
return "Result from A";
});
CompletableFuture<string> futureB = CompletableFuture.supplyAsync(() -> {
try { Thread.sleep(50); } catch (InterruptedException e) {}
return "Result from B";
});
Function<string string> transformer = s -> s.toUpperCase();
// applyToEither 接收另一个 Stage,thenApply 接收函数
CompletableFuture<string> result = futureA.applyToEither(futureB)
.thenApply(transformer);
System.out.println(result.get());
}
}</string></string></string></string>
运行上述代码,由于 futureB 耗时更短(50ms vs 100ms),applyToEither 会优先捕获 futureB 的结果。随后 thenApply 将字符串转换为大写。预期输出为 RESULT FROM B。如果交换睡眠时长,输出将变为 RESULT FROM A。这证明了 applyToEither 负责竞争,而函数式接口 transformer 负责后续处理。
当两个 Stage 类型不同如何处理
如果两个 CompletionStage 的泛型类型不一致,例如一个是 CompletableFuture,另一个是 CompletableFuture,则不能直接使用 applyToEither,因为其要求两个 Stage 的类型参数必须相同。此时需要先统一类型,通常通过 thenApply 将两者都转换为同一类型(如 String 或 Object),然后再调用 applyToEither。这种场景下,函数式接口的作用是进行类型适配而非简单的值转换。

import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public class TypeMismatchDemo {
public static void main(String[] args) throws Exception {
CompletableFuture<integer> intFuture = CompletableFuture.supplyAsync(() -> 42);
CompletableFuture<string> strFuture = CompletableFuture.supplyAsync(() -> "Hello");
// 先将 Integer 转换为 String,以便与 String 类型的 Stage 兼容
CompletableFuture<string> convertedIntFuture = intFuture.thenApply(String::valueOf);
Function<string string> merger = s -> "Combined: " + s;
// 现在两者都是 CompletableFuture<string>,可以应用 applyToEither
CompletableFuture<string> result = convertedIntFuture.applyToEither(strFuture)
.thenApply(merger);
System.out.println(result.get());
}
}</string></string></string></string></string></integer>
在此示例中,intFuture 被 thenApply(String::valueOf) 转换为 CompletableFuture。这样 convertedIntFuture 和 strFuture 类型一致,可以传入 applyToEither。最终 merger 函数式接口对获胜的结果进行拼接。预期输出取决于哪个 Stage 先完成,可能是 Combined: Hello 或 Combined: 42。关键点在于类型统一必须在 applyToEither 之前完成。
applyToEither 遇到异常时的行为
当参与竞争的任意一个 CompletionStage 抛出异常时,applyToEither 返回的新阶段也会以异常状态结束。如果先完成的 Stage 正常返回,后完成的 Stage 即使后来也抛出异常,也不会影响已确定的结果,除非你显式处理了异常。若想在任一 Stage 失败时提供默认值,需结合 exceptionally 方法。例如,stage1.applyToEither(stage2).exceptionally(ex -> "Default")。这里的 ex 是 Throwable 类型的参数,函数式接口 Function 用于生成替代值。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











