Java 强制执行期望的可完成的未来行为
我正在玩CompletableFuture链,偶然发现了一个具有意外行为的情况(至少对我来说):如果在Java 强制执行期望的可完成的未来行为,java,completable-future,Java,Completable Future,我正在玩CompletableFuture链,偶然发现了一个具有意外行为的情况(至少对我来说):如果在中传递了异常CompletableFuture。然后调用compose(),则生成的CompletableFuture将在原始异常包装在CompletionException中完成。如果没有一个例子,可能很难理解: public static <T> CompletableFuture<T> exceptional(Throwable error) { Compl
中传递了异常CompletableFuture。然后调用compose()
,则生成的CompletableFuture将在原始异常包装在CompletionException
中完成。如果没有一个例子,可能很难理解:
public static <T> CompletableFuture<T> exceptional(Throwable error) {
CompletableFuture<T> future = new CompletableFuture<>();
future.completeExceptionally(error);
return future;
}
public static void main(String[] args) {
CompletableFuture<Void> exceptional = exceptional(new RuntimeException());
exceptional
.handle((result, throwable) -> {
System.out.println(throwable);
// java.lang.RuntimeException
System.out.println(throwable.getCause());
// null
return null;
});
CompletableFuture
.completedFuture(null)
.thenCompose(v -> exceptional)
.handle((result, throwable) -> {
System.out.println(throwable);
// java.util.concurrent.CompletionException: java.lang.RuntimeException
System.out.println(throwable.getCause());
// java.lang.RuntimeException
return null;
});
}
公共静态CompletableFuture异常(可丢弃错误){
CompletableFuture=新的CompletableFuture();
未来。异常完全(错误);
回归未来;
}
公共静态void main(字符串[]args){
CompletableFuture Exception=Exception(新的RuntimeException());
异常
.handle((结果,可丢弃)->{
System.out.println(可丢弃);
//java.lang.RuntimeException
System.out.println(throwable.getCause());
//空的
返回null;
});
完全未来
.completedFuture(空)
.然后组合(v->Exception)
.handle((结果,可丢弃)->{
System.out.println(可丢弃);
//java.util.concurrent.CompletionException:java.lang.RuntimeException
System.out.println(throwable.getCause());
//java.lang.RuntimeException
返回null;
});
}
当然,我希望处理相同的RuntimeException
,无论在链中前后进行了多少次转换。我有两个问题:
- 这是预期行为吗
- 除了手动展开外,我是否有任何选项来保持原始异常的传递
- 的JavaDoc是:
返回一个新的CompletionStage,当此阶段正常完成时,将使用此阶段作为提供函数的参数执行该阶段。有关异常完成的规则,请参阅文档
以及接口状态的定义:
[…]在所有其他情况下,如果一个阶段的计算突然终止,出现(未检查的)异常或错误,则所有要求完成的相关阶段也会异常完成,并保留异常作为其原因。[……]
当
然后撰写
返回一个相关阶段时,这是预期的行为
事实上,除了CompletionException
之外,只有当您使用completeeexceptionaly()
、cancel()
等方法显式地完成CompletableFuture
时,您才能拥有其他异常。即使是supplyAsync()
等方法也会包装您的异常
我认为没有任何其他选项可以访问原始异常,因为使用getCause()
打开它已经很容易了。如果确实需要经常这样做,可以编写一个助手方法,例如:
public static <T, U> BiFunction<? super T, Throwable, ? extends U>
unwrappingCompletionException(BiFunction<? super T, Throwable, ? extends U> fn) {
return (t, u) -> {
if (u instanceof CompletionException) {
return fn.apply(t, u.getCause());
}
return fn.apply(t, u);
};
}
CompletableFuture
.completedFuture(null)
.thenCompose(v -> exceptional)
.handle(unwrappingCompletionException((result, throwable) -> {
[…]
}));