JAVA/TESTSGO →TS →

Concurrency

CompletableFuture: join は失敗をCompletionExceptionで包む

非同期処理が失敗すると、joinは元の例外をそのままではなくCompletionExceptionのcauseとして送出します。

Java 8+
pitfallconcurrencycompletablefutureexceptionasync
01 / 01 proof
SourceCompletableFutureFailures.java
Java 21
package examples.concurrency;

import java.util.concurrent.CompletableFuture;

public final class CompletableFutureFailures {
  private CompletableFutureFailures() {}

  public static String joinFailedFuture() {
    return CompletableFuture.<String>failedFuture(new IllegalArgumentException("invalid input"))
        .join();
  }
}
Source✓Test
TestCompletableFutureFailuresTest.java
Java 21
package examples.concurrency;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
import static org.junit.jupiter.api.Assertions.assertThrows;

import java.util.concurrent.CompletionException;
import org.junit.jupiter.api.Test;

class CompletableFutureFailuresTest {
  @Test
  void joinは非同期処理の失敗をCompletionExceptionで包む() {
    CompletionException exception =
        assertThrows(CompletionException.class, CompletableFutureFailures::joinFailedFuture);

    assertInstanceOf(IllegalArgumentException.class, exception.getCause());
    assertEquals("invalid input", exception.getCause().getMessage());
  }
}
01Assertion

このテストで確認できること

  • failedFutureのjoinはCompletionExceptionを送出する
  • getCauseで元のIllegalArgumentExceptionを取り出せる

joinを呼ぶ境界ではCompletionExceptionを考慮し、原因例外を確認してアプリケーションの例外方針へ変換します。

OBSERVED RESULTexpected exception: join → CompletionException actual: CompletionException expected cause: IllegalArgumentException actual: IllegalArgumentException

Static index

教材を検索

テーマ、タグ、テスト名、カテゴリ、Javaバージョンで絞り込みます。

55件の教材を表示しています。

Language · Java 8+String: == と equals@Test 同じ内容のStringはequalsで等しいLanguage · Java 11+String: isBlank は空白だけの文字列も判定する@Test 空白だけのStringはisBlankでtrueになるLanguage · Java 8+StringBuilder: append は同じインスタンスを変更する@Test StringBuilderのappendは同じインスタンスを返すCollections · Java 8+ArrayList: add は元の List を変更する@Test addすると元のListに要素が追加されるCollections · Java 9+List.of: 変更できない List を作る@Test ListOfが返すListには要素を追加できないCollections · Java 8+Arrays.asList: 配列を共有する固定長 List@Test ArraysAsListで変更した要素は元の配列にも反映されるCollections · Java 8+Map: computeIfAbsent で既定値を登録する@Test 存在しないキーには既定値が登録されるNumbers · Java 8+Integer: == はキャッシュ範囲でだけ同じ参照になり得る@Test Integerの128は参照比較では等しくないNumbers · Java 8+BigDecimal: equals と compareTo は scale の扱いが違う@Test scaleが異なっても数値として同じBigDecimalはcompareToで等しいLanguage · Java 16+record: 同じ構成要素なら equals で等しい@Test 同じ値を持つrecordはequalsで等しくなるLanguage · Java 8+equals と hashCode: HashSetで値を見つけるための契約@Test equalsとhashCodeを実装した値はHashSetで検索できるFunctional · Java 8+Optional: orElse で既定値を返す@Test emptyのOptionalは既定値を返すFunctional · Java 8+Optional: orElse と orElseGet は既定値の評価タイミングが違う@Test orElseはOptionalに値があっても引数を評価するFunctional · Java 8+Optional: map と flatMap はOptionalのネストをどう扱うかが違う@Test flatMapはOptionalを返す関数の結果を平坦化するFunctional · Java 8+Stream: filter で条件に合う値を残す@Test filterは正の数だけを残すFunctional · Java 8+Stream: 中間操作は終端操作まで評価されない@Test Streamは終端操作までmapを評価しないFunctional · Java 16+Stream.toList: 変更できない List を返す@Test StreamToListが返すListには要素を追加できないError Handling · Java 8+Exception: assertThrows で例外の契約を確かめる@Test ゼロで割るとArithmeticExceptionが送出されるError Handling · Java 8+Exception: checked と unchecked は呼び出し側の契約が違う@Test 不正な引数はuncheckedのIllegalArgumentExceptionを送出するGenerics · Java 8+generics: ? extends Number で数値型を受け取る@Test IntegerのListをNumberとして合計できるGenerics · Java 8+generics: ? extends は読む側、? super は書く側の境界@Test superはNumberのListへIntegerを追加できるDate / Time · Java 8+Date / Time: LocalDate と LocalDateTime は時刻情報の有無が違う@Test LocalDateから作るLocalDateTimeはその日の開始時刻になるError Handling · Java 7+try-with-resources: 複数resourceは逆順にcloseされる@Test tryWithResourcesは処理後にcloseを必ず呼ぶCollections · Java 8+HashMap: 可変のキーを変更すると登録済みの値を検索できない@Test HashMapへ登録した後にキーを変更すると同じキーで検索できないNumbers · Java 8+BigDecimal: 除算の丸めモードで同じ式の結果が変わる@Test BigDecimalの除算は指定した丸めモードで結果が変わるFunctional · Java 8+Optional: ofNullable はnullをemptyにし、ofは例外にする@Test ofNullableはnullをemptyのOptionalへ変換するCollections · Java 8+HashMap: 衝突は処理できるがequalsとhashCodeの不整合は検索を壊す@Test equalsがtrueでもhashCodeが違うキーでは登録済みの値を検索できないNumbers · Java 8+BigDecimal: doubleから作ると意図しない桁が入る@Test doubleから作るBigDecimalは10進数の0point1と厳密には一致しないFunctional · Java 8+Optional: フィールドや引数にするとnullとemptyが二重になる@Test Optional型の引数にもnullを渡せるため不在表現が二重になるLanguage · Java 8+String: length はUTF-16のコード単位を数える@Test 絵文字はUTF16のlengthでは2だがcodePointCountでは1になるLanguage · Java 8+String: 見た目が同じUnicode文字列でもequalsが異なる@Test 見た目が同じでも合成済みと結合文字の文字列はequalsで異なるCollections · Java 8+HashMap: get のnullだけでは未登録とnull値を区別できない@Test getだけではnull値のキーと未登録キーを区別できないCollections · Java 9+List.of: null要素を許可しない@Test ListOfはnull要素を持つListを作れないFunctional · Java 8+Stream: 終端操作の後に同じStreamは使えない@Test 終端操作を実行したStreamは再利用できないDate / Time · Java 8+ZonedDateTime: plusDays と plusHours は夏時間で異なる@Test 夏時間開始日のplusDaysとplusHoursは同じ時刻にならないLanguage · Java 16+record: 配列の構成要素は値ではなく参照で比較される@Test 同じ要素の別配列を持つrecordはequalsで等しくならないLanguage · Java 21+sealed class: 許可した型だけを網羅的にswitchできる@Test sealed型の全許可サブタイプをswitchで分岐できるConcurrency · Java 21+virtual thread: 通常のThreadLocalは親から引き継がれない@Test virtualThreadには親スレッドの通常のThreadLocal値が引き継がれないConcurrency · Java 8+CompletableFuture: join は失敗をCompletionExceptionで包む@Test joinは非同期処理の失敗をCompletionExceptionで包むConcurrency · Java 8+ConcurrentHashMap: nullキーとnull値を許可しない@Test ConcurrentHashMapはnullキーとnull値を許可しないGenerics · Java 8+generics: 型引数は実行時に消去される@Test 異なる型引数のArrayListは実行時には同じクラスであるCollections · Java 8+TreeSet: Comparatorが0を返す値は同じ要素として扱う@Test Comparatorが0を返す異なる値はTreeSetで同じ要素として扱われるCollections · Java 10+defensive copy: 入力Listの後続変更から状態を守る@Test constructorは入力Listをコピーして後からの変更を受けないCollections · Java 10+Collections: unmodifiable view と immutable copy は共有状態が違う@Test unmodifiableViewは元のListの後続変更を反映するFunctional · Java 8+Collectors.toMap: 重複キーにはmerge規則が必要@Test toMapは同じキーがあるとmerge関数なしでは例外にするLanguage · Java 8+enum: 定数は同じインスタンスとして比較できる@Test valueOfで得たenum定数は宣言済み定数と同じインスタンスであるLanguage · Java 8+annotation: RUNTIME保持ならreflectionから読める@Test RUNTIME保持のannotationはreflectionから取得できるLanguage · Java 8+class initialization: static fieldは宣言順に初期化される@Test staticFieldは宣言順に初期化されるLanguage · Java 8+overload: wideningはboxingより先に選ばれる@Test primitiveのshortはboxingよりwideningを優先してlongを選ぶLanguage · Java 8+varargs: 配列を渡すと同じ配列を受け取る@Test 配列を渡すvarargsは配列をコピーせず同じ配列を受け取るNumbers · Java 8+unboxing: nullのIntegerを計算するとNullPointerExceptionになる@Test nullのIntegerを計算するとunboxing時にNullPointerExceptionになるLanguage · Java 8+String pool: リテラルとnew Stringは参照共有が違う@Test 同じリテラルはpoolの同じ参照を共有するConcurrency · Java 8+ExecutorService: submitした例外はFutureで観測する@Test submitしたタスクの例外はFutureのgetでExecutionExceptionとして観測するConcurrency · Java 8+interruption: Thread.interruptedは割り込みフラグをclearする@Test ThreadInterruptedは現在の割り込み状態を返してclearするConcurrency · Java 8+synchronized: 同じスレッドは同じmonitorへ再入できる@Test 同じスレッドは保持中のsynchronizedモニタを再入できる