CompletableFuture doesn't work if it saved to variable

Why does it not work when I save the future to variable before configuration steps?

@Test
void simple() {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(this::throwException)
            .exceptionally(throwable -> HANDLED);
    assertEquals(HANDLED, future.join());
}

@Test
void withVar() {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(this::throwException);
    future.exceptionally(throwable -> HANDLED);
    assertEquals(HANDLED, future.join());
}

private String throwException() {
    if (true) {
        throw new RuntimeException(FAIL);
    }
    return SUCCESS;
}

simple() is fine, but withVar() doesn't work:

java.util.concurrent.CompletionException: java.lang.RuntimeException: FAIL

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:183)`
Caused by: java.lang.RuntimeException: FAIL
    at ru.dionisis.resttest.ComplitableFutureTest.throwException(ComplitableFutureTest.java:35)
    at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1700)
    ... 6 more`

A similar problem was with RestTemplateBuilder, when I wanted make otional init one field

Upvotes: 1

Views: 3562

Answers (1)

Michael
Michael

Reputation: 44150

From the JavaDoc for exceptionally: "Returns a new CompletableFuture...". It does not mutate the CompleteableFuture that it's called upon.

You are creating a new CompleteableFuture with new behaviour and then throwing that away, then joining on the original CompleteableFuture which does not have that behaviour.

It's like saying that String::toUpperCase doesn't work in this example. It does work, but the result is thrown away.

String foo = "bar";
foo.toUpperCase();
assertEquals("BAR", foo);

Upvotes: 2

Related Questions