web-dev-qa-db-ja.com

CompletableFutureから例外をスロー

私は次のコードを持っています:

// 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()の呼び出し元に例外をスローします。

22
ayushgp

コードでは、非同期操作の結果を後で同じメソッドで使用することを提案しているため、CompletionExceptionを処理する必要があるため、それを処理する1つの方法は

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であるため、考えられるすべての型を既に処理したにもかかわらず、コンパイラはその型を処理することを要求します。簡単な解決策は、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 futureを介して渡された元の形式のカスタムexceptionのみをスローします。競合状態を避けるために、a futureをクエリする前に、exceptionが完了していることを確認する必要があることに注意してください(最初にjoin()を呼び出すように)。

34
Holger

CompletableFutureを使用した例外処理で他の方法を探している人向け

以下は、整数への解析エラーを処理する例のいくつかの方法です。

1。 handleメソッドの使用-例外のデフォルト値を提供できます

CompletableFuture correctHandler = CompletableFuture.supplyAsync(() -> "A")
            .thenApply(Integer::parseInt)
            .handle((result, ex) -> {
                if (null != ex) {
                    ex.printStackTrace();
                    return 0;
                } else {
                    System.out.println("HANDLING " + result);
                    return result;
                }
            })
            .thenAcceptAsync(s -> {
                System.out.println("CORRECT: " + s);
            });

2。 exceptionallyメソッドを使用する-handleに似ていますが、より冗長ではありません

CompletableFuture parser = CompletableFuture.supplyAsync(() -> "1")
                .thenApply(Integer::parseInt)
                .exceptionally(t -> {
                    t.printStackTrace();
                    return 0;
                }).thenAcceptAsync(s -> System.out.println("CORRECT value: " + s));

3。 whenComplete Methodの使用-これを使用すると、そのトラックでメソッドが停止し、次のthenAcceptAsyncは実行されません

CompletableFuture correctHandler2 = CompletableFuture.supplyAsync(() -> "A")
                .thenApply(Integer::parseInt)
                .whenComplete((result, ex) -> {
                    if (null != ex) {
                        ex.printStackTrace();
                    }
                })
                .thenAcceptAsync(s -> {
                    System.out.println("When Complete: " + s);
                });

4。 completeExceptionallyを介した例外の伝播

public static CompletableFuture<Integer> converter(String convertMe) {
        CompletableFuture<Integer> future = new CompletableFuture<>();
        try {
            future.complete(Integer.parseInt(convertMe));
        } catch (Exception ex) {
            future.completeExceptionally(ex);
        }
        return future;
    }
17
mel3kings

あなたはそれをRuntimeExceptionにラップして投げるべきだと思う:

 throw new RuntimeException(ex);

または、多くの小さなユーティリティが役立ちます:

static class Wrapper extends RuntimeException {

    private Wrapper(Throwable throwable) {
        super(throwable);
    }

    public static Wrapper wrap(Throwable throwable) {
        return new Wrapper(throwable);
    }

    public Throwable unwrap() {
        return getCause();
    }
}


 public static void go() {
    CompletableFuture<String> a = CompletableFuture.supplyAsync(() -> {
        try {
            throw new Exception("Just because");
        } catch (Exception ex) {
            throw Wrapper.wrap(ex);
        }
    });

    a.join();
}

そして、unwrap that ..

 try {
        go();
 } catch (Wrapper w) {
        throw w.unwrap();
 }
3
Eugene

他の人の答えが非常にいい場合でも。しかし、CompletableFutureでチェック例外をスローする別の方法を提供します。

IF別のスレッドでCompletableFutureを呼び出したくない場合は、匿名クラスを使用して次のように処理できます。

CompletableFuture<A> a = new CompletableFuture<A>() {{
    try {
        complete(someObj.someFunc());
    } catch (ServerException ex) {
        completeExceptionally(ex);
    }
}};

IF別のスレッドでCompletableFutureを呼び出す場合、匿名クラスを使用してそれを処理することもできますが、runAsyncでメソッドを実行します。

CompletableFuture<A> a = new CompletableFuture<A>() {{
    CompletableFuture.runAsync(() -> {
        try {
            complete(someObj.someFunc());
        } catch (ServerException ex) {
            completeExceptionally(ex);
        }
    });
}};
2
holi-java