Saif
Saif

Reputation: 3452

How to throw exception when Try.of() fails?

I want to throw Exceptions that are extended from Exception if Try.ofCallable() fails.

I have a callable of the type:

final Callable<MyResponse> decoratedCallable =
    circuitBreakerService.getDecoratedMethod(
        myArg1, 
        () -> myFunction(myArg1, myArg2, myArg3)
    );

I am trying something like this:

Try.ofCallable(decoratedCallable).onFailure(throwable -> {
    if (throwable instanceof CallNotPermittedException) {
        throw new MyRuntimeExceptionA("msg1", throwable);
    } else {
        throw new MyRuntimeExceptionB("msg2", throwable);
    }
});

This works (the function that wraps the above two statements throws the correct exception MyRuntimeExceptionA and MyRuntimeExceptionB) if both MyRuntimeExceptionA and MyRuntimeExceptionB extend RuntimeException, but not if they extend Exception.

If they extend Exception then I am not able to throw them from the main function. The IDE asks to wrap them in try/catch - which I don't want.

Upvotes: 2

Views: 3782

Answers (2)

N&#225;ndor Előd Fekete
N&#225;ndor Előd Fekete

Reputation: 7098

You have two options. You can throw when you try to unwrap the Try by getting the value with the following code:

Try.ofCallable(decoratedCallable)
    .getOrElseThrow(throwable -> {
        if (throwable instanceof CallNotPermittedException) {
            return new MyExceptionA("msg1", throwable);
        } else {
            return new MyExceptionB("msg2", throwable);
        }
    })

or move out the error mapping code to before unwrapping with a similar code:

Try.ofCallable(decoratedCallable)
    .mapFailure(
        Case(
            $(instanceOf(CallNotPermittedException.class)),
            throwable -> new MyExceptionA("msg1", throwable)
        ),
        Case($(), throwable -> new MyExceptionB("msg2", throwable))
    )
    .get()

Both solutions will only throw when unrwapping, so if you want to throw early, you will have to unwrap early.

Otherwise, I would take the advice others posted in comments not to throw exceptions if you are using Try. The whole point in using Try is to work with total functions instead of partial functions that can throw exceptions.

Upvotes: 5

Marcio Lucca
Marcio Lucca

Reputation: 380

I don't know much about vavr, but looking in the javadoc for the library, you can see the onFailure method takes a Consumer<? super Throwable> as a parameter. The problem is that consumers do not declare checked exceptions, so you will never be able throw checked exceptions from your lambda.

That being said, what I generally do in these cases is I create a "wrapping" class that will accept checked exceptions, all this wrapping class will do is catch any checked exceptions and wrap them in a runtime exception. For example:

public class ThrowingConsumerHelper {
    public static <T> Consumer<T> throwingConsumer(
            ThrowingConsumer<T> consumer) {
        return object -> {
            try {
                consumer.accept(object);
            } catch (Throwable t) {
                throw new RuntimeException(t);
            }
        };
    }

    @FunctionalInterface
    public interface ThrowingConsumer<T> {
        void accept(T t) throws Exception;
    }
}

And then use this like this:

import static ThrowingConsumerHelper.throwingConsumer;

    public static void main(String[] args) {
        onFailure(throwingConsumer(object -> { throw new Exception("Bug"); }));
    }
    
    public static void onFailure(Consumer<? super Throwable> consumer) {
        // Do something
    }

Upvotes: 1

Related Questions