2016-03-28 31 views
14

我剛開始探索的Java 8的一些併發特性的一件事弄得我有點是這兩個靜態方法:CompletableFuture supplyAsync

CompletableFuture<Void> runAsync(Runnable runnable) 
CompletableFuture<U> supplyAsync(Supplier<U> supplier) 

不要任何人知道爲什麼他們選擇使用的接口供應商?使用Callable是否更自然?這與Runnable的類比是否會返回值?那是因爲供應商不會拋出無法處理的異常嗎?

+0

因爲他們剛剛從C#,這通常命名爲'行動<>'和'不同Func <>'接口,它們完成相同的工作,但不會讓程序員感到困惑。所以他們擴大了具有相同簽名但不同名稱的方法的功能接口。他們可能認爲這會幫助程序員提供一個合作的心理模型,但它不會:這只是一個小小的PITA。 – davidbak

回答

7

簡短的回答

不,這不是更自然的CompletableFuture.supplyAsync使用Callable,而不是Supplier。這個論點幾乎完全是關於語義的,所以如果你之後仍然不確定,那就沒關係。

龍答案

CallableSupplier功能接口/ SAM類型在功能上幾乎相等(原諒雙關語),但其來源和用途不同。

Callable被創建爲java.util.concurrent包的一部分。這個包來自Java 8中關於lambda表達式的巨大變化,最初集中在幫助您編寫併發代碼的一系列工具上,而不會偏離經典的動態多線程模型。

Callable的主要用途是抽象可以在不同線程中執行並返回結果的動作。從Callable的Javadoc中:

Callable接口類似於Runnable,在兩者都 設計用於一個其實例可能受 另一個線程執行。

Supplier被作爲java.util.function包的一部分創建的。該包作爲Java 8上述更改的組成部分。它提供了可以由lambda表達式和方法引用定位的常用函數類型。

一種這樣的類型是沒有參數返回結果的函數(即提供某種類型函數或函數)。

那麼爲什麼Supplier而不是Callable

CompletableFuture是添加到由在Java中8的上述變化的啓發,允許顯影劑以構建在功能,隱含並行方式他的代碼java.util.concurrent包的一部分,而不是在它明確地處理併發。

它的supplyAsync方法需要一種方法來提供特定類型的結果,並且對結果更感興趣,而不是爲了達到這個結果而採取的行動。它也不一定關心異常完成(另請參閱下面的...段落)。

但是,如果Runnable用於無參數,無結果功能接口,不應使用Callable用於無參數,單結果功能接口?

不一定。

對於沒有參數且不返回結果(因此完全通過外部上下文中的副作用操作)的函數的抽象不包括在java.util.function中。這意味着(有些惱人地)Runnable被用在任何需要這種功能接口的地方。

那麼Callable.call()可以拋出的支票Exception怎麼辦?

這是CallableSupplier之間預期語義差異的一個小符號。

A Callable是一個動作,可以在另一個線程中執行,並允許您檢查其副作用的執行結果。如果一切順利,您會得到特定類型的結果,但由於在執行某些操作時(特別是在多線程環境中)可能會出現異常情況,因此您可能還需要定義和處理這些異常情況。

另一方面,A Supplier是您依賴於提供某種類型的對象的函數。作爲Supplier的直接消費者,例外情況不一定是您的責任。這是因爲:

  1. ...功能接口通常用於在創建或變異數據,以及處理Exception可以均是一個獨立的階段,多階段過程定義一個特定的階段,如果你不在乎
  2. ...明確處理Exception小號顯著減少的功能接口,Lambda表達式和方法表達能力引用
+0

我認爲你的論點主要區別在於語義方面,而不是實踐方面是正確的。在語義上使用名爲_CallAsync_的方法並不意味着它會返回結果。 –

+0

IMO對於同一個功能_(例如'call()'vs'get()')具有不同方法分別命名的函數接口的想法是不妥當的,會導致很多煩惱。其中兩個特別是:試圖記住你需要調用的方法是什麼,然後不容易發送你需要Callable等方法的供應商等等。你描述的預期「語義」都是而且與編譯器強制執行的任何內容無關,在Java文檔中根本沒有描述或根本沒有描述。 C#做得更好。 – davidbak

相關問題