CompletableFuture join() 與 get() 指南
一、簡介
在Java的並發程式設計中, CompletableFuture是一個強大的工具,可以讓我們編寫非阻塞程式碼。使用CompletableFuture時,我們會遇到兩種常見的方法: join()和get() 。這兩種方法都用於在計算完成後檢索結果,但它們有一些關鍵的差異。
在本教程中,我們將探討這兩種方法之間的差異。
2.CompletableFuture CompletableFuture
在深入探討join()和get()之前,讓我們先簡單回顧一下CompletableFuture是什麼。 CompletableFuture表示非同步計算的未來結果。與回調等傳統方法相比,它提供了一種以更具可讀性和可管理性的方式編寫非同步程式碼的方法。讓我們來看一個範例來說明CompletableFuture的用法。
首先,讓我們建立一個CompletableFuture :
CompletableFuture<String> future = new CompletableFuture<>();
接下來,讓我們用一個值來完成 future:
future.complete("Hello, World!");
最後,我們使用join()或get()擷取值:
String result = future.join(); // or future.get();
System.out.println(result); // Output: Hello, World!
3. join()方法
join()方法是檢索CompletableFuture結果的直接方法。它等待計算完成,然後傳回結果。如果計算遇到異常, join()會拋出未經檢查的異常,特別是CompletionException 。
這是join()的語法:
public T join()
我們回顧一下join()方法的特點:
- 計算完成後回傳結果
- 拋出未經檢查的異常 -
CompletionException- 如果完成CompletableFuture涉及的任何計算導致異常 - 由於
CompletionException是未經檢查的異常,因此不需要在方法簽章中明確處理或聲明
4. get()方法
另一方面, get()方法檢索計算結果,並在計算遇到錯誤時拋出檢查異常。 get()方法有兩種變體:一種無限期等待,另一種等待指定逾時。
讓我們回顧一下get()的兩種變體的語法:
public T get() throws InterruptedException, ExecutionException
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException
我們再看看get()方法的特性:
- 計算完成後回傳結果
- 引發已檢查異常,可能是
InterruptedException、ExecutionException或TimeoutException - 需要在方法簽名中明確處理或聲明已檢查異常
get()方法繼承自CompletableFuture實作的Future介面。 Java 5 中引入的Future介面表示非同步計算的結果。它定義了get()方法來檢索結果並處理計算過程中可能發生的異常。
當 Java 8 中引入CompletableFuture時,它被設計為與現有的Future介面相容,以確保與現有程式碼庫的向後相容性。這需要在CompletableFuture中包含get()方法。
5. 比較: join()與get()
讓我們總結一下join()和get()之間的主要差異:
| 方面 | join() | get() |
| 異常類型 | 拋出CompletionException (未選取) | 拋出InterruptedException 、 ExecutionException和TimeoutException (已檢查) |
| 例外處理 | 未經檢查,無需聲明或捕獲 | 已檢查,必須申報或抓獲 |
| 超時支持 | 不支援超時 | 支援超時 |
| 起源 | 特定於CompletableFuture | 繼承自Future介面 |
| 使用建議 | 新代碼的首選 | 為了與舊版本 |
6. 測試
讓我們加入一些測試來確保我們對join()和get()的理解是正確的:
@Test
public void givenJoinMethod_whenThrow_thenGetUncheckedException() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Test join");
assertEquals("Test join", future.join());
CompletableFuture<String> exceptionFuture = CompletableFuture.failedFuture(new RuntimeException("Test join exception"));
assertThrows(CompletionException.class, exceptionFuture::join);
}
@Test
public void givenGetMethod_whenThrow_thenGetCheckedException() {
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Test get");
try {
assertEquals("Test get", future.get());
} catch (InterruptedException | ExecutionException e) {
fail("Exception should not be thrown");
}
CompletableFuture<String> exceptionFuture = CompletableFuture.failedFuture(new RuntimeException("Test get exception"));
assertThrows(ExecutionException.class, exceptionFuture::get);
}
七、結論
在這篇快速文章中,我們了解到join()和get()都是用來檢索CompletableFuture結果的方法,但它們處理異常的方式不同。 join()方法會拋出未經檢查的異常,這使得當我們不想明確處理異常時更容易使用。另一方面, get()方法拋出檢查異常,提供更詳細的異常處理和逾時支援。一般來說,由於其簡單性,新程式碼應該首選join() ,而get()仍然可用於舊程式碼相容性。
本文中的範例程式碼可以在 GitHub 上找到。