2016-10-31 5 views
1

私はCompletableFutureチェーンで遊んでいて、予期せぬ動作(私にとっては少なくとも)の状況に遭遇しました:例外的なCompletableFutureが.thenCompose()呼び出しで渡された場合、結果のCompletableFutureは元の例外がラップされて終了しますCompletionExceptionにあります。たとえばなしで理解するのは難しいかもしれ:もちろん希望するCompletableFutureの動作を強制する

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; 
      }); 
} 

私は関係なく、チェーン内の前か後だったどのように多くの変換同じRuntimeExceptionに対処しないように期待していました。私は2つの質問があります:

  • これは予想される動作ですか?
  • 手動アンラッピングを除いて、元の例外を引き継ぐためのオプションがありますか?

答えて

2

thenCompose()のJavaDocは次のとおりです。

このステージが正常に完了し、新しいCompletionStageを返し、供給関数の引数として、この段階で実行されます。例外的完了を扱うルールについては、CompletionStageのドキュメントを参照してください。他のすべての場合において

[...]、ステージの計算は、(オフ)例外またはエラーで突然その完了を必要とするすべての依存段階を終了した場合:

及び界面状態の定義同様に例外的に完了し、その原因として例外を保持するCompletionExceptionがあります。 [...] thenComposeとして

、これは予想される動作である依存段階、を返します。あなたの例外をラップするようsupplyAsync()ようcompleteExceptionally()cancel()などであってもメソッドと同様の方法で明示的にCompletableFutureを完了したときに

は実際には、あなたがCompletionException以外の何かを持っている可能性がある唯一の例です。

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) -> { 
      […] 
     })); 
関連する問題