MST
星途 面试题库

面试题:Java中thenCompose方法的异常处理

在使用CompletableFuture的thenCompose方法组合两个异步任务时,如果第一个任务抛出异常,如何优雅地处理这个异常,使得第二个任务不会被执行且能正确捕获异常信息?请给出具体代码实现,并说明不同处理方式的优缺点。
39.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

代码实现

import java.util.concurrent.CompletableFuture;

public class CompletableFutureExceptionHandling {
    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> {
            if (Math.random() < 0.5) {
                throw new RuntimeException("第一个任务抛出异常");
            }
            return "第一个任务结果";
        })
       .thenCompose(result -> {
            System.out.println("第二个任务开始执行,结果: " + result);
            return CompletableFuture.completedFuture("第二个任务结果");
        })
       .exceptionally(ex -> {
            System.out.println("捕获到异常: " + ex.getMessage());
            return null;
        })
       .thenAccept(System.out::println);
    }
}

不同处理方式及优缺点

  1. 使用exceptionally方法
    • 优点:简单直接,在链式调用中可以方便地捕获前序任务抛出的异常,并返回一个默认值或者进行一些异常处理操作。例如在上述代码中,exceptionally捕获到第一个任务抛出的异常并打印异常信息,同时返回null,避免第二个任务执行。
    • 缺点:如果异常处理逻辑较为复杂,代码可读性可能会受影响,尤其是在多个CompletableFuture链式调用时,异常处理代码会与正常业务逻辑混合在一起。
  2. 使用handle方法
    • 优点handle方法既可以处理正常结果,也可以处理异常情况,统一在一个方法内进行逻辑处理,使代码结构相对紧凑。
    • 缺点:对于只想单纯处理异常而不涉及正常结果处理的场景,handle方法可能会显得过于冗余,因为必须同时处理正常和异常情况。示例代码如下:
CompletableFuture.supplyAsync(() -> {
    if (Math.random() < 0.5) {
        throw new RuntimeException("第一个任务抛出异常");
    }
    return "第一个任务结果";
})
.handle((result, ex) -> {
    if (ex != null) {
        System.out.println("捕获到异常: " + ex.getMessage());
        return null;
    }
    System.out.println("第二个任务开始执行,结果: " + result);
    return "第二个任务结果";
})
.thenAccept(System.out::println);
  1. 使用whenComplete方法(结合isCompletedExceptionally方法)
    • 优点whenComplete方法在任务完成(正常完成或异常完成)时都会执行,可以在其中根据isCompletedExceptionally判断任务是否异常完成,进行不同的处理逻辑,灵活性较高。
    • 缺点whenComplete方法不返回新的CompletableFuture,不能直接在其内部处理异常并返回处理结果,需要额外的变量或者其他方式来传递处理结果,增加了代码的复杂性。示例代码如下:
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() < 0.5) {
        throw new RuntimeException("第一个任务抛出异常");
    }
    return "第一个任务结果";
});
future.whenComplete((result, ex) -> {
    if (ex != null) {
        System.out.println("捕获到异常: " + ex.getMessage());
    } else {
        System.out.println("第二个任务开始执行,结果: " + result);
        // 这里需要另起一个CompletableFuture来模拟第二个任务
        CompletableFuture.supplyAsync(() -> "第二个任务结果")
        .thenAccept(System.out::println);
    }
});