Java 使用CompletableFuture调整异常的正确方法

Java 使用CompletableFuture调整异常的正确方法,java,exception-handling,java-8,completable-future,Java,Exception Handling,Java 8,Completable Future,我正在研究链接CompletableFuture以适应一个异常。虽然我有一些有用的东西,但我不明白它为什么有用 @Test public void futureExceptionAdapt() throws ExecutionException, InterruptedException { class SillyException extends Exception { } class AdaptedException extends Exception { AdaptedEx

我正在研究链接CompletableFuture以适应一个异常。虽然我有一些有用的东西,但我不明白它为什么有用

@Test
public void futureExceptionAdapt() throws ExecutionException, InterruptedException {
    class SillyException extends Exception { }
    class AdaptedException extends Exception { AdaptedException(SillyException silly) { } }

    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        sleepForThreeSeconds();
        if (true)
            throw new CompletionException(new SillyException());
        return 5;
    })

    .thenApplyAsync(val -> val * 10);

    CompletableFuture<Integer> futureAdaptException = future.exceptionally((t) -> {
        if (t instanceof CompletionException && t.getCause() instanceof SillyException) {
            System.out.println("adapt SillyException to AdaptedException");
            SillyException silly = (SillyException) t.getCause();
            future.obtrudeException(new AdaptedException(silly));
        }
        return null;
    });

    try {
        future.get();
        fail("future should have failed with an exception");
    } catch (ExecutionException e) {
        assertTrue("got exception: " + getCauseClass(e),
                   e.getCause() instanceof AdaptedException);
    }

    // I am not sure why the above succeeds
    // because I did not call futureAdaptException.get()
    // According to the IDE futureAdaptException is an unused variable at this point

    assertTrue("expect futureAdaptException to have failed with AdaptedException but instead the result is: " + futureAdaptException.get(),
               futureAdaptException.isCompletedExceptionally());
}

private static void sleepForThreeSeconds() {
    try {
        Thread.sleep(3000L);
    } catch (InterruptedException e) {
    }
}

private static String getCauseClass(Throwable t) {
    if (t.getCause() == null) return "null";
    return t.getCause().getClass().getName();
}

调用
future.get()
时,不会“调用”FutureAdaptiveException。所发生的情况是,您使用
future.excellective()
创建了它,因此它将在
future
异常完成时自动触发

因此,即使未使用
futureAdaptException
(因此可以删除该变量),
exception()
仍然有副作用

excellective()
获得的
CompletableFuture
将根据您在传入函数中所做的操作成功或失败。如果希望它失败,仍然可以再次抛出异常:

CompletableFuture<Integer> futureAdaptException = future.exceptionally((t) -> {
    if (t instanceof CompletionException && t.getCause() instanceof SillyException) {
        System.out.println("adapt SillyException to AdaptedException");
        SillyException silly = (SillyException) t.getCause();
        final AdaptedException ex = new AdaptedException(silly);
        future.obtrudeException(ex);
        throw new CompletionException(ex);
    }
    return null;
});
CompletableFuture-futureAdaptException=future.exception((t)->{
if(t instanceof CompletionException&&t.getCause()instanceof SillyException){
System.out.println(“adapt SillyException to AdaptedException”);
SillyException愚蠢=(SillyException)t.getCause();
最终适配异常ex=新适配异常(愚蠢);
未来障碍例外(ex);
抛出新的CompletionException(ex);
}
返回null;
});
请注意,您可能应该避免使用
obtrudeeexception()
,因为这是不确定的。事实上,我很惊讶你的第一个断言成功了。如果成功,则由
excellective()
返回的
CompletableFuture
将以与原始结果相同的结果完成,因此您应该使用该结果


我肯定认为这是由于JDK中的一个bug造成的。如果在
异常()
中添加
睡眠三秒钟()
,测试仍然通过。但是,如果您在
future.get()
之前添加睡眠超过3秒,则断言将失败,您将获得原始异常。如果在完成之前调用
get()
,它似乎也会等待
异常()
执行。为了更好地理解这一点,我发了帖子。

现在它变得非常混乱。我写了另一个测试,通过链接构建未来,另一个测试通过两个单独的语句构建未来,行为是不同的。future=CompletableFuture.supplySync(睡眠3秒并返回5)。然后是applySync(返回val*3)。睡眠5秒,future.get()返回15。但是future=CompletableFuture.supplyAsync(睡眠3秒,返回5秒);future=future.thenApplySync(返回值*3)。睡眠5秒,以后再睡。get()返回5。@snaran很难从你评论中的伪代码中分辨出来,但你可能在测试中的某个地方犯了错误……不过,我明白我的错误了。如果您使用future=future.thenappyasync(…),而不是future.thenappyasync(…),那么future.get()将对链进行求值,即在示例中返回15。新代码似乎与原始问题/问题无关。最好将此作为一个单独的问题发布,并附带一个具体的可回答问题。不过,我没有发现代码中有任何错误:这些测试应该会成功。记住,对链式方法的每次调用都会返回一个新的
CompletableFuture
CompletableFuture<Integer> futureAdaptException = future.exceptionally((t) -> {
    if (t instanceof CompletionException && t.getCause() instanceof SillyException) {
        System.out.println("adapt SillyException to AdaptedException");
        SillyException silly = (SillyException) t.getCause();
        final AdaptedException ex = new AdaptedException(silly);
        future.obtrudeException(ex);
        throw new CompletionException(ex);
    }
    return null;
});