Как создать исключение при сбое Try.of()?

#java #vavr #resilience4j

#java #vavr #отказоустойчивость 4j

Вопрос:

Я хочу генерировать исключения, которые расширяются от Exception if Try.ofCallable() fails.

У меня есть вызываемый тип:

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

Я пытаюсь сделать что-то вроде этого:

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

Это работает (функция, которая обертывает два приведенных выше оператора, выдает правильное исключение MyRuntimeExceptionA и MyRuntimeExceptionB), если оба MyRuntimeExceptionA и MyRuntimeExceptionB расширяются RuntimeException , но не если они расширяются Exception .

Если они расширяются Exception , то я не могу выбросить их из основной функции. IDE просит обернуть их в try / catch, чего я не хочу.

Комментарии:

1. Есть ли особая причина, по которой, если вы используете Vavr, вы не возвращаете Try<MyResponse> ?

2. В этом (казалось бы, сокращенном) контексте кажется, что вы попали в ловушку захватывающих новых библиотек, не полностью понимая, почему они существуют или должны использоваться. Попробуйте написать тот же код на стандартной Java и посмотрите, как это работает.

3. Основная цель использования Try — НЕ создавать исключений. Немного больше контекста о том, почему, можно найти в документации Vavr о побочных эффектах

Ответ №1:

У вас есть два варианта. Вы можете генерировать исключение при попытке развернуть Try , получая значение с помощью следующего кода:

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

или переместите код сопоставления ошибок в перед развертыванием с аналогичным кодом:

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

Оба решения будут генерироваться только при отмене, поэтому, если вы хотите выполнить ранний запуск, вам придется выполнить ранний запуск.

В противном случае я бы воспользовался советом, который другие опубликовали в комментариях, не создавать исключений, если вы используете Try . Весь смысл использования Try заключается в работе с полными функциями вместо частичных функций, которые могут генерировать исключения.

Ответ №2:

Я мало что знаю о vavr, но, просмотрев библиотеку javadoc, вы можете увидеть, что метод OnFailure принимает значение Consumer<? super Throwable> в качестве параметра. Проблема в том, что потребители не объявляют проверяемые исключения, поэтому вы никогда не сможете генерировать проверенные исключения из вашего lambda.

При этом, что я обычно делаю в этих случаях, так это создаю «обтекающий» класс, который будет принимать проверенные исключения, все, что этот обтекающий класс будет делать, это перехватывать любые проверенные исключения и оборачивать их в исключение во время выполнения. Например:

 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;
    }
}
  

А затем используйте это следующим образом:

 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
    }