有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java是一种合适的方法,可以适应未来可完成的异常

我正在研究链接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()时,为什么会调用futureAdaptException

第二,有没有办法让futureAdaptException.get()以期望的异常失败?因为如果不需要的话,我不想创建一个虚拟对象。不能从lambda修改futureAdaptException

或者也许有更好的方法来适应例外情况。或者可能有exceptionally()的一个变体,它将对象保持在异常阶段(似乎return null导致futureAdaptException将对象置于值为null的正常阶段)。或者我们不应该在CompletableFuture中修改异常

    @Test
public void futureStrangeBehaviorFromChaining1() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("sleep for 3 seconds then return 5");
        sleepForThreeSeconds();
        return 5;
    });

    future.thenApplyAsync(val -> {
        System.out.println("multiply by 3");
        return val*3;
    });

    sleepForFiveSeconds();

    assertEquals(5, future.get().intValue());

    sleepForFiveSeconds();

    assertEquals(5, future.get().intValue());
}

@Test
public void futureStrangeBehaviorFromChaining2() throws ExecutionException, InterruptedException {
    CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
        System.out.println("sleep for 3 seconds then return 5");
        sleepForThreeSeconds();
        return 5;
    })

    .thenApplyAsync(val -> {
        System.out.println("multiply by 3");
        return val*3;
    });

    sleepForFiveSeconds();

    assertEquals(15, future.get().intValue());

    sleepForFiveSeconds();

    assertEquals(15, future.get().intValue());
}

共 (1) 个答案

  1. # 1 楼答案

    当您调用future.get()时,futureAdaptException不是“调用的”。结果是,您使用future.exceptionally()创建了它,因此当future异常完成时,它将自动触发

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

    exceptionally()获得的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;
    });
    

    请注意,您可能应该避免使用obtrudeException(),因为这是不确定的。事实上,我很惊讶你的第一个断言成功了。如果成功,由exceptionally()返回的CompletableFuture将以与原始结果相同的结果完成,因此您应该使用该结果

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