3

The question is rather simple: I'm looking for an elegant way of using CompletableFuture#exceptionally alongside with CompletableFuture#supplyAsync. This is what does not work:

private void doesNotCompile() {
    CompletableFuture<String> sad = CompletableFuture
            .supplyAsync(() -> throwSomething())
            .exceptionally(Throwable::getMessage);
}

private String throwSomething() throws Exception {
    throw new Exception();
}

I thought the idea behind exceptionally() was precisely to handle cases where an Exception is thrown. Yet if I do this it works:

private void compiles() {
    CompletableFuture<String> thisIsFine = CompletableFuture.supplyAsync(() -> {
        try {
            throwSomething();
            return "";
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }).exceptionally(Throwable::getMessage);
}

I could work with that, but it looks horrible and makes things harder to maintain. Is there not a way to keep this clean which doesn't require transforming all the Exception into RuntimeException ?

payne
  • 4,691
  • 8
  • 37
  • 85
  • If you want to use the first option, simply change the return type of `throwSomething()` from `void` to `String`. – Slaw Nov 20 '20 at 22:18
  • Sorry, that's a mistake of mine while I was writing up this simplified version. I edited OP with your suggestion to point out that it doesn't solve the problem. – payne Nov 20 '20 at 22:32
  • 1
    Ah, sorry. I forgot you were dealing with _checked_ exceptions. Basically, the problem is `Supplier#get()` is not declared to throw `Exception`. Maybe something here will help you: [Java 8 Lambda function that throws exception?](https://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception). – Slaw Nov 20 '20 at 22:37

1 Answers1

2

This might not be a super popular library, but we use it (and from times to times I do some work there too; minor though) internally: NoException. It is really, really nicely written for my taste. This is not the only thing it has, but definitely covers your use case:

Here is a sample:

import com.machinezoo.noexception.Exceptions;
import java.util.concurrent.CompletableFuture;

public class SO64937499 {

    public static void main(String[] args) {
        CompletableFuture<String> sad = CompletableFuture
            .supplyAsync(Exceptions.sneak().supplier(SO64937499::throwSomething))
            .exceptionally(Throwable::getMessage);
    }

    private static String throwSomething() throws Exception {
        throw new Exception();
    }
}

Or you can create these on your own:

final class CheckedSupplier<T> implements Supplier<T> {

    private final SupplierThatThrows<T> supplier;

    CheckedSupplier(SupplierThatThrows<T> supplier) {
        this.supplier = supplier;
    }

    @Override
    public T get() {
        try {
            return supplier.get();
        } catch (Throwable exception) {
            throw new RuntimeException(exception);
        }
    }
}



@FunctionalInterface
interface SupplierThatThrows<T> {

    T get() throws Throwable;
}

And usage:

 CompletableFuture<String> sad = CompletableFuture
        .supplyAsync(new CheckedSupplier<>(SO64937499::throwSomething))
        .exceptionally(Throwable::getMessage);
payne
  • 4,691
  • 8
  • 37
  • 85
Eugene
  • 117,005
  • 15
  • 201
  • 306
  • While this looks like a nice solution, we are restricted to using a private nexus which doesn't host this kind of libraries for security reasons. – payne Nov 23 '20 at 16:30
  • @payne see EDIT – Eugene Nov 23 '20 at 16:42
  • That's looking better. That, however, wouldn't let me use the exception as an argument, right? Because I would need to then check what was the type of Exception to handle the different cases differently. – payne Nov 23 '20 at 16:53