2016-10-31 77 views
1

我在玩CompletableFuture連鎖店,偶然發現了意外行爲的情況(至少對我來說):如果在.thenCompose()調用中傳遞了一個例外的CompletableFuture,則生成的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

的Javadoc thenCompose()是:

返回,當該階段正常完成,與此階段作爲參數所提供的功能執行的新CompletionStage。關於異常完成的規則​​,請參閱CompletionStage文檔。

和界面態的定義:

[...]在所有其他情況下,如果一個階段的計算與(未選中)異常或錯誤而突然終止,那麼所有的從屬級要求其完成完全例外,以CompletionException作爲其原因。 [...]

由於thenCompose回報依賴階段,這是預期的行爲

事實上,在這你可以有東西比其他CompletionException唯一情況是,當你與方法完成CompletableFuture明確像completeExceptionally()cancel()等方法,即使如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) -> { 
      […] 
     }));