1. ホーム
  2. java

CompletableFutureからの例外投下

2023-10-07 19:46:48

質問

次のようなコードがあります。

// How to throw the ServerException?
public void myFunc() throws ServerException{
    // Some code
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try {
            return someObj.someFunc();
        } catch(ServerException ex) {
            // throw ex; gives an error here.
        }
    }));
    // Some code
}

someFunc() を投げる ServerException . ここでこれを処理せずに、例外をスローするのは someFunc() の呼び出し元に対して myFunc() .

どのように解決するのですか?

あなたのコードでは、非同期操作の結果を同じメソッド内で後で使っているようなので、その場合は CompletionException いずれにせよ、これを処理する一つの方法は

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) { throw new CompletionException(ex); }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        try {
            throw ex.getCause();
        }
        catch(Error|RuntimeException|ServerException possible) {
            throw possible;
        }
        catch(Throwable impossible) {
            throw new AssertionError(impossible);
        }
    }
    // some code using resultOfA
}

の非同期処理の内部で投げられた全ての例外は、その例外が発生した時点で終了します。 Supplier の中で発生した例外は CompletionException を呼び出すと join を除いて ServerException を除いて、すでに CompletionException .

の原因を投げ直すと CompletionException のサブクラスなど、チェックされていない例外に直面する可能性があります。 Error または RuntimeException または、カスタムチェックした例外 ServerException . 上記のコードでは、これらすべてをマルチキャッチで処理し、再スローしています。の戻り値の型が宣言されているので getCause()Throwable である場合、コンパイラはすでにすべての可能な型を処理しているにもかかわらず、その型を処理するように要求します。簡単な解決策は、この実際に不可能な throwable を AssertionError .

別の方法として、カスタム例外のために別の結果未来を使用することもできます。

public void myFunc() throws ServerException {
    // Some code
    CompletableFuture<ServerException> exception = new CompletableFuture<>();
    CompletableFuture<A> a = CompletableFuture.supplyAsync(() -> {
        try { return someObj.someFunc(); }
        catch(ServerException ex) {
            exception.complete(ex);
            throw new CompletionException(ex);
        }
    });
    // Some code running in parallel to someFunc()

    A resultOfA;
    try {
        resultOfA = a.join();
    }
    catch(CompletionException ex) {
        if(exception.isDone()) throw exception.join();
        throw ex;
    }

    // some code using resultOfA
}

この解決策は、すべての「予期しない」投げ物をラップされた状態で投げ直しますが、カスタムの ServerException を介して渡された元のフォームの exception を介して渡された元の形である。注意することは a が完了したことを確認する必要があります (例えば join() を最初に呼ぶようなもの) が完了したら、その前に exception に問い合わせる前に、レースコンディションを回避するために