只需将检查的异常包装成CompletionException
CompletableFuture
使用时的异常处理要考虑的另一点completeExceptionally()
是,确切的异常将在其中可用,handle()
但whenComplete()
在CompletionException
调用join()
或转发到任何下游阶段时将被包装。
因此,应用于下游阶段的a handle()
or将看到 a而不是原始的,并且必须查看其原因以找到原始异常。exceptionally()
CompletionException
此外,RuntimeException
任何操作(包括supplyAsync()
)抛出的任何东西也被包裹在 a 中CompletionException
,除非它已经是 a CompletionException
。
考虑到这一点,最好在安全的情况下使用它并让您的异常处理程序解开CompletionException
s.
如果你这样做了,那么在 上设置确切的(检查的)异常就没有意义了,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
.