在 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<t r></t> 类型的函数式接口实例。

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());
}
}
运行上述代码,由于 futureB 耗时更短(50ms vs 100ms),applyToEither 会优先捕获 futureB 的结果。随后 thenApply 将字符串转换为大写。预期输出为 RESULT FROM B。如果交换睡眠时长,输出将变为 RESULT FROM A。这证明了 applyToEither 负责竞争,而函数式接口 transformer 负责后续处理。
立即学习“Java免费学习笔记(深入)”;
当两个 Stage 类型不同如何处理
如果两个 CompletionStage 的泛型类型不一致,例如一个是 CompletableFuture<integer></integer>,另一个是 CompletableFuture<string></string>,则不能直接使用 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());
}
}
在此示例中,intFuture 被 thenApply(String::valueOf) 转换为 CompletableFuture<string></string>。这样 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<throwable t></throwable> 用于生成替代值。


















