1

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.

Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
Saif
  • 2,530
  • 3
  • 27
  • 45
  • 2
    Is there a particular reason why, if you're using Vavr, you aren't returning a `Try`? – chrylis -cautiouslyoptimistic- Aug 14 '20 at 00:44
  • In this (seemingly reduced) context it seems you've fallen into the trap of exciting new libraries without fully realizing why they exist or should be used. Try writing the same code with standard Java and see how it works. – Torben Aug 14 '20 at 04:43
  • 2
    The main goal of using `Try` is to NOT throw exceptions. A bit more context about why, can be found in the Vavr documentation about [side-effects](https://www.vavr.io/vavr-docs/#_side_effects) – Hinse ter Schuur Aug 14 '20 at 07:42

2 Answers2

5

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.

Nándor Előd Fekete
  • 6,988
  • 1
  • 22
  • 47
1

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
    }
Marcio Lucca
  • 370
  • 2
  • 10