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

Ond*_* K. 6 java exception java-8 completable-future

假设我想在遇到特定异常时使用某个值进行恢复,否则返回带有异常的失败未来。我希望是这样的:

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";
}
Run Code Online (Sandbox Code Playgroud)

如果fetchValue函数会抛出一个已检查的异常,我想在链式方法中处理它。return throwable和都试过了throw throwable,但都没有编译。是否CompletableFuture为这种情况提供了任何解决方案?我知道Function作为exceptionally方法参数的接口不会抛出任何异常 - 在这种情况下,我只想返回已经失败的未来。我想找到一个使用 Java 8 的解决方案。

Hol*_*ger 7

在这种情况下,不可能收到受检异常,因为前一个阶段是基于 a 的Supplier,不允许抛出受检异常。

因此,您可以处理所有未经检查的异常并AssertionError为应该不可能的 throwable引发一个:

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

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

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");
}
Run Code Online (Sandbox Code Playgroud)

我得到

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

所以我可以CompletionException用于包装任意的 throwables,利用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");
}
Run Code Online (Sandbox Code Playgroud)

我得到

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");
}
Run Code Online (Sandbox Code Playgroud)

这在堆栈跟踪中略有不同,但对接收/捕获异常的代码没有影响,因为在任何一种情况下,它都是CompletionException一个IllegalStateException.

所以回到你的问题的例子,你可以使用

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

由于CompletionException是 a RuntimeException,此代码处理它并避免将 a 包装CompletionException在 another 中CompletionException。否则,模式将是

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