将已检查异常包装成CompletionException
在使用completeExceptionally()
时,需要考虑的另一点是,在CompletableFuture
中处理异常时,在handle()
和whenComplete()
中会提供确切的异常,但在调用join()
或转发到任何下游阶段时,该异常将被包装在CompletionException
中。
因此,应用于下游阶段的handle()
或exceptionally()
将看到一个CompletionException
而不是原始异常,并且必须查找其原因以找到原始异常。
此外,任何操作(包括supplyAsync()
)抛出的RuntimeException
也会被包装在CompletionException
中,除非它已经是一个CompletionException
。
考虑到这一点,最好采取安全措施,让您的异常处理程序取消包装CompletionException
。
如果这样做,那么在
CompletableFuture
上设置确切的(已检查的)异常就没有意义了,
直接将已检查的异常包装在 CompletionException
中会更简单。
Supplier<Integer> numberSupplier = () -> {
try {
return SupplyNumbers.sendNumbers();
} catch (Exception e) {
throw new CompletionException(e);
}
};
与
Holger的方法进行比较,我使用了两种解决方案来改编您的代码(
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");
}
}
输出:
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
如您所见,唯一的区别在于,在“customWrap()”情况下,
whenComplete()
会在
thenApply()
之前看到原始异常。在
thenApply()
之后以及所有其他情况中,原始异常都被包装。
最令人惊讶的是,在“Simple wrap”情况下,
get()
将取消包装
CompletionException
,并用
ExecutionException
替换它。