CompletableFuture#exceptionally重新抛出检查的异常

时间:2019-04-01 11:22:34

标签: java exception java-8 completable-future

假设如果我遇到特定的异常,我想以某个值恢复,否则返回带有异常的失败的将来。我希望这样的事情:

[[x[0], x[1] + 's', x[2] + '1'] for x in a]

如果IndexError: list index out of range 函数会抛出一个 checked 异常,我想在链接方法中对其进行处理。我已经尝试过public static void main(String[] args) { CompletableFuture .supplyAsync(FuturesExample::fetchValue) .exceptionally(throwable -> { if (throwable instanceof RuntimeException) { return "All good"; } throw throwable; // does not compile }); } public static String fetchValue() { // code that potentially throws an exception return "value"; } fetchValue,但是都没有编译。 return throwable可以为这种情况提供任何解决方案吗?我知道throw throwable方法的参数CompletableFuture接口不会引发任何异常-在这种情况下,我只想返回已经失败的将来。我想找到一个使用Java 8的解决方案。

1 个答案:

答案 0 :(得分:1)

在这种情况下,由于上一阶段基于Supplier,因此不允许接收已检查的异常。

因此,您可以处理所有未检查的异常,并为应该不可能的可抛出对象引发AssertionError

CompletableFuture
    .supplyAsync(FuturesExample::fetchValue)
    .exceptionally(throwable -> {
        if (throwable instanceof RuntimeException) {
            return "All good";
        }
        if(throwable instanceof Error) throw (Error)throwable;
        throw new AssertionError(throwable);
    });

否则,您可能会认为后续阶段以及join()的调用者都将获得除包裹在CompletionException中的CancellationExceptionCompletionException之外的所有异常。例如。当我使用

public static void main(String[] args) {
    CompletableFuture<String> f = CompletableFuture
        .supplyAsync(FuturesExample::fetchValue)
        .exceptionally(throwable -> {
            if(throwable instanceof RuntimeException) {
                throw (RuntimeException)throwable;
            }
            throw new Error();
        });
    f.whenComplete((s,t) -> {
        if(t != null) {
            System.err.println("in whenComplete handler ");
            t.printStackTrace();
        }
    });
    System.err.println("calling join()");
    f.join();
}
public static String fetchValue() {
    throw new IllegalStateException("a test is going on");
}

我明白了

in whenComplete handler 
java.util.concurrent.CompletionException: java.lang.IllegalStateException: a test is going on
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.IllegalStateException: a test is going on
    at FuturesExample.fetchValue(FuturesExample.java:23)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
    ... 6 more
calling join()
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.IllegalStateException: a test is going on
    at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:314)
    at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:319)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1702)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.IllegalStateException: a test is going on
    at FuturesExample.fetchValue(FuturesExample.java:23)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
    ... 6 more

因此,我利用CompletionException不再被包裹这一事实,可以使用CompletionException包裹任意可抛出的东西。所以如果我使用

public static void main(String[] args) {
    CompletableFuture<String> f = CompletableFuture
        .supplyAsync(FuturesExample::fetchValue)
        .exceptionally(throwable -> {
            if(throwable instanceof CompletionException)
                throwable = throwable.getCause();
            System.err.println("wrapping '"+throwable+"' inside exceptionally");
            throw new CompletionException(throwable);
        });
    f.whenComplete((s,t) -> {
        if(t != null) {
            System.err.println("in whenComplete handler ");
            t.printStackTrace();
        }
    });
    System.err.println("calling join()");
    f.join();
}
public static String fetchValue() {
    throw new IllegalStateException("a test is going on");
}

我明白了

wrapping 'java.lang.IllegalStateException: a test is going on' inside exceptionally
in whenComplete handler 
java.util.concurrent.CompletionException: java.lang.IllegalStateException: a test is going on
    at FuturesExample.lambda$main$0(FuturesExample.java:12)
    at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:986)
    at java.base/java.util.concurrent.CompletableFuture$UniExceptionally.tryFire(CompletableFuture.java:970)
    at java.base/java.util.concurrent.CompletableFuture.unipush(CompletableFuture.java:589)
    at java.base/java.util.concurrent.CompletableFuture.uniExceptionallyStage(CompletableFuture.java:1002)
    at java.base/java.util.concurrent.CompletableFuture.exceptionally(CompletableFuture.java:2307)
    at FuturesExample.main(FuturesExample.java:8)
Caused by: java.lang.IllegalStateException: a test is going on
    at FuturesExample.fetchValue(FuturesExample.java:24)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
calling join()
Exception in thread "main" java.util.concurrent.CompletionException: java.lang.IllegalStateException: a test is going on
    at FuturesExample.lambda$main$0(FuturesExample.java:12)
    at java.base/java.util.concurrent.CompletableFuture.uniExceptionally(CompletableFuture.java:986)
    at java.base/java.util.concurrent.CompletableFuture$UniExceptionally.tryFire(CompletableFuture.java:970)
    at java.base/java.util.concurrent.CompletableFuture.unipush(CompletableFuture.java:589)
    at java.base/java.util.concurrent.CompletableFuture.uniExceptionallyStage(CompletableFuture.java:1002)
    at java.base/java.util.concurrent.CompletableFuture.exceptionally(CompletableFuture.java:2307)
    at FuturesExample.main(FuturesExample.java:8)
Caused by: java.lang.IllegalStateException: a test is going on
    at FuturesExample.fetchValue(FuturesExample.java:24)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.exec(CompletableFuture.java:1692)
    at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
    at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
    at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
    at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
    at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

在堆栈跟踪中稍有不同,但是对接收/捕获异常的代码没有影响,因为在两种情况下,它都是CompletionException包裹了IllegalStateException

所以回到问题的示例,您可以使用

CompletableFuture
    .supplyAsync(FuturesExample::fetchValue)
    .exceptionally(throwable -> {
        if (throwable instanceof RuntimeException) { // includes CompletionException
            return "All good";
        }
        throw new CompletionException(throwable);
    });

由于CompletionExceptionRuntimeException,因此此代码对其进行处理,并避免将CompletionException包装在另一个CompletionException中。否则,模式将为

    .exceptionally(throwable -> {
        if (some condition) {
            return some value;
        }
        throw throwable instanceof CompletionException?
            (CompletionException)throwable: new CompletionException(throwable);
    });