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 楼答案
当您调用
future.get()
时,futureAdaptException
不是“调用的”。结果是,您使用future.exceptionally()
创建了它,因此当future
异常完成时,它将自动触发因此,即使
futureAdaptException
未使用(因此可以删除该变量),exceptionally()
仍然有副作用从
exceptionally()
获得的CompletableFuture
将成功或失败,这取决于您在传入函数中执行的操作。如果希望它失败,仍然可以再次抛出异常:请注意,您可能应该避免使用
obtrudeException()
,因为这是不确定的。事实上,我很惊讶你的第一个断言成功了。如果成功,由exceptionally()
返回的CompletableFuture
将以与原始结果相同的结果完成,因此您应该使用该结果我肯定认为这是由于JDK中的一个错误。如果在
exceptionally()
中添加sleepForThreeSeconds()
,测试仍然通过。但是,如果在future.get()
之前添加了超过3秒的睡眠,断言将失败,您将获得原始异常。如果在完成之前调用get()
,它似乎也在等待exceptionally()
执行。为了更好地理解这一点,我发布了this question