只需将检查的异常包装成CompletionException
CompletableFuture使用时的异常处理要考虑的另一点completeExceptionally()是,确切的异常将在其中可用,handle()但whenComplete()在CompletionException调用join()或转发到任何下游阶段时将被包装。
因此,应用于下游阶段的a handle()or将看到 a而不是原始的,并且必须查看其原因以找到原始异常。exceptionally()CompletionException
此外,RuntimeException任何操作(包括supplyAsync())抛出的任何东西也被包裹在 a 中CompletionException,除非它已经是 a CompletionException。
考虑到这一点,最好在安全的情况下使用它并让您的异常处理程序解开CompletionExceptions.
如果你这样做了,那么在 上设置确切的(检查的)异常就没有意义了,CompletableFuture并且直接将检查的异常包装起来要简单得多CompletionException:
Supplier<Integer> numberSupplier = () -> {
try {
return SupplyNumbers.sendNumbers();
} catch (Exception e) {
throw new CompletionException(e);
}
};
为了将此方法与Holger 的方法进行比较,我使用 2 个解决方案调整了您的代码(simpleWrap()上面customWrap()是 Holger 的代码):
public class TestCompletableFuture {
public static void main(String args[]) {
TestCompletableFuture testF = new TestCompletableFuture();
System.out.println("Simple wrap");
testF.handle(testF.simpleWrap());
System.out.println("Custom wrap");
testF.handle(testF.customWrap());
}
private void handle(CompletableFuture<Integer> future) {
future.whenComplete((x1, y) -> {
System.out.println("Before thenApply(): " + y);
});
future.thenApply(x -> x).whenComplete((x1, y) -> {
System.out.println("After thenApply(): " + y);
});
try {
future.join();
} catch (Exception e) {
System.out.println("Join threw " + e);
}
try {
future.get();
} catch (Exception e) {
System.out.println("Get threw " + e);
}
}
public CompletableFuture<Integer> simpleWrap() {
Supplier<Integer> numberSupplier = () -> {
try {
return SupplyNumbers.sendNumbers();
} catch (Exception e) {
throw new CompletionException(e);
}
};
return CompletableFuture.supplyAsync(numberSupplier);
}
public CompletableFuture<Integer> customWrap() {
CompletableFuture<Integer> f = new CompletableFuture<>();
ForkJoinPool.commonPool().submit(
(Runnable & CompletableFuture.AsynchronousCompletionTask) () -> {
try {
f.complete(SupplyNumbers.sendNumbers());
} catch (Exception ex) {
f.completeExceptionally(ex);
}
});
return f;
}
}
class SupplyNumbers {
public static Integer sendNumbers() throws Exception {
throw new Exception("test"); // just for working sake its not correct.
}
}
输出:
Simple wrap
After thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Before thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Join threw java.util.concurrent.CompletionException: java.lang.Exception: test
Get threw java.util.concurrent.ExecutionException: java.lang.Exception: test
Custom wrap
After thenApply(): java.util.concurrent.CompletionException: java.lang.Exception: test
Before thenApply(): java.lang.Exception: test
Join threw java.util.concurrent.CompletionException: java.lang.Exception: test
Get threw java.util.concurrent.ExecutionException: java.lang.Exception: test
正如您会注意到的,唯一的区别是在案例whenComplete()之前看到了原始异常。之后,以及在所有其他情况下,原始异常被包装。thenApply()customWrap()thenApply()
最令人惊讶的是,get()它将在“简单包装”案例中解开CompletionException包装,并将其替换为ExecutionException.