Search code examples
javaexceptioncompletable-future

Map exception in completable future to a different exception type?


I'm using java 8's completable futures and I'd like to be able take an exception that is throwing by the future and transform it to a different exception.

All the composite stuff I've tried seems to get short circuited once an exception occurs.

Using a scala future, for example, I can do something like this:

scala.concurrent.Future<Object> translatedException = ask.recover(new Recover<Object>() {
            @Override public Object recover(final Throwable failure) throws Throwable {
                if (failure instanceof AskTimeoutException) {
                    throw new ApiException(failure);
                }

                throw failure;
            }
        }, actorSystem.dispatcher());

and I'd like to be able to mimic that in a future composite block in java. Is this possible?


Solution

  • You can use CompletableFuture#handle(BiFunction). For example

    CompletableFuture<String> ask = CompletableFuture.supplyAsync(() -> {
        throw new IndexOutOfBoundsException();
    });
    CompletableFuture<String> translatedException = ask.handle((r, e) -> {
        if (e != null) {
            if (e instanceof IndexOutOfBoundsException) {
                throw new IllegalArgumentException();
            }
            // fallback
            if (e instanceof RuntimeException) {
                throw (RuntimeException) e;
            }
            throw new RuntimeException(e);
        }
        return r;
    });
    

    If ask completed with an exception, then translatedException will complete with a potentially transformed exception. Otherwise, it will have the same success result value.

    Concerning my comment in the code, the handle method expects a BiFunction whose apply method is not declared to throw a Throwable. As such, the lambda body cannot itself throw a Throwable. The parameter e is of type Throwable so you can't throw it directly. You can cast it to RuntimeException if you know it's of that type, or you can wrap it in a RuntimeException and throw that.