Java 强制执行期望的可完成的未来行为

Java 强制执行期望的可完成的未来行为,java,completable-future,Java,Completable Future,我正在玩CompletableFuture链,偶然发现了一个具有意外行为的情况(至少对我来说):如果在中传递了异常CompletableFuture。然后调用compose(),则生成的CompletableFuture将在原始异常包装在CompletionException中完成。如果没有一个例子,可能很难理解: public static <T> CompletableFuture<T> exceptional(Throwable error) { Compl

我正在玩CompletableFuture链,偶然发现了一个具有意外行为的情况(至少对我来说):如果在
中传递了异常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) -> {
                  […]
              }));