Подтвердить что ты не робот

Как обернуть проверенные исключения, но сохранить исходные исключения в Java

У меня есть код, который может вызывать как проверенные, так и исполняемые исключения.

Я хотел бы поймать проверенное исключение и обернуть его исключением во время выполнения. Но если выбрано RuntimeException, мне не нужно его обертывать, поскольку это уже исключение для выполнения.

Решение, которое у меня есть, немного накладно и не является "опрятным":

try {
  // some code that can throw both checked and runtime exception
} catch (RuntimeException e) {
  throw e;
} catch (Exception e) {
  throw new RuntimeException(e);
}

Любая идея для более элегантного способа?

4b9b3361

Ответ 1

Я использую "слепой" повтор, чтобы исключить проверенные исключения. Я использовал это для прохождения через API Streams, где я не могу использовать lambdas, которые выбрали проверенные исключения. например, у нас есть функциональные интерфейсы ThrowingXxxxx, поэтому проверенное исключение можно передать через.

Это позволяет мне поймать проверенное исключение в вызывающем абоненте, естественно, без необходимости знать, что вызываемый должен был передать его через интерфейс, который не разрешил проверенные исключения.

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
  throw rethrow(e);
}

В вызывающем методе я могу снова объявить проверенное исключение.

public void loadFile(String file) throws IOException {
   // call method with rethrow
}

/**
 * Cast a CheckedException as an unchecked one.
 *
 * @param throwable to cast
 * @param <T>       the type of the Throwable
 * @return this method will never return a Throwable instance, it will just throw it.
 * @throws T the throwable as an unchecked throwable
 */
@SuppressWarnings("unchecked")
public static <T extends Throwable> RuntimeException rethrow(Throwable throwable) throws T {
    throw (T) throwable; // rely on vacuous cast
}

Существует множество различных вариантов обработки исключений. Мы используем некоторые из них.

https://vanilla-java.github.io/2016/06/21/Reviewing-Exception-Handling.html

Ответ 2

Guava Throwables.propagate() делает именно это:

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    throw Throwables.propagate(e);
}

UPDATE: этот метод теперь устарел. Подробнее см. эту страницу.

Ответ 3

Не совсем.

Если вы сделаете это много, вы можете спрятать его в вспомогательный метод.

static RuntimeException unchecked(Throwable t){
    if (t instanceof RuntimeException){
      return (RuntimeException) t;
    } else if (t instanceof Error) { // if you don't want to wrap those
      throw (Error) t;
    } else {
      return new RuntimeException(t);
    }
}

try{
 // ..
}
catch (Exception e){
   throw unchecked(e);
}

Ответ 4

У меня есть специально скомпилированный файл .class, содержащий следующее:

public class Thrower {
    public static void Throw(java.lang.Throwable t) {
        throw t;
    }
}

Он просто работает. Компилятор java обычно отказывается компилировать это, но верификатор байткода вообще не заботится.

Класс используется аналогично Питеру Лоури:

try {
  // some code that can throw both checked and runtime exception

} catch (Exception e) {
    Thrower.Throw(e);
}

Ответ 5

Вы можете переписать то же самое с помощью оператора instanceof

try {
    // some code that can throw both checked and runtime exception
} catch (Exception e) {
    if (e instanceof RuntimeException) {
        throw e;
    } else {
        throw new RuntimeException(e);
    }
}

Однако ваше решение выглядит лучше.

Ответ 6

Проблема в том, что Exception слишком широк. Вы должны точно знать, какие возможны проверенные исключения.

try {
    // code that throws checked and unchecked exceptions
} catch (IOException | SomeOtherException ex) {
    throw new RuntimeException(ex);
}

Причины, по которым это не помогло бы, выявили бы более глубокие проблемы, которые следует решать вместо этого:

Если метод объявляет, что он throws Exception, то он слишком широк. Зная, что "что-то может пойти не так", и никакая дополнительная информация бесполезна для вызывающего. Этот метод должен использовать определенные классы исключений в значимой иерархии или, при необходимости, использовать исключенные исключения.

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

Конечно, могут быть исключения из правила. Объявление метода throws Exception может быть вполне разумным, если оно потребляется какой-то сквозной структурой (например, JUnit или AspectJ или Spring), а не содержит API для других пользователей.

Ответ 7

Обычно я использую один и тот же тип структуры кода, но конденсирую его до одной строки в один из нескольких раз, когда троичный оператор действительно улучшает код:

try {
  // code that can throw
}
catch (Exception e) {
  throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);
}

Это не требует дополнительных методов или блоков catch, поэтому мне это нравится.