Захват исключения, вложенного в другое исключение

Я хочу поймать исключение, которое вложено в другое исключение. В настоящее время я делаю это так:

} catch (RemoteAccessException e) {
    if (e != null && e.getCause() != null && e.getCause().getCause() != null) {
        MyException etrp = (MyException) e.getCause().getCause();
        ...
    } else {
        throw new IllegalStateException("Error at calling service 'service'");
    }
}

Есть ли способ сделать это более эффективным и элегантным?

Ответы

Ответ 1

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

Элегантное решение - избавиться от вложенности исключений. Либо не создавайте цепочки исключений, во-первых, либо (выборочно) разворачивайте и перебрасывайте вложенные исключения дальше в стек.

Исключения обычно бывают вложенными по 3 причинам:

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

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

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

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

Во втором случае вам, вероятно, следует развернуть исключения, как только элемент управления пройдет проблемный метод API.

В третьем случае вам следует пересмотреть свою стратегию обработки исключений; т.е. сделать это правильно 2.


1 - Действительно, одна из полулегитимных причин для этого исчезла из-за введения синтаксиса catch с множественными исключениями в Java 7.

2 - Не меняйте свои методы API на throws Exception.Это только усугубляет ситуацию.Теперь вы должны либо "обрабатывать", либо распространять Exception каждом вызове методов.Это рак...

Ответ 2

Вы должны добавить некоторые проверки, чтобы увидеть, действительно ли e.getCause().getCause() MyException. В противном случае этот код выдает ClassCastException. Я бы, вероятно, написал это как:

} catch(RemoteAccessException e) {
    if(e.getCause() != null && e.getCause().getCause() instanceof MyException) {
        MyException ex = (MyException)e.getCause().getCause();
        // Do further useful stuff
    } else {
        throw new IllegalStateException("...");
    }
}

Ответ 4

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

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

Первый (e!= null) не нужен.

И вы можете изменить третий лучше на e.getCause(). getCause() instanceof MyException)

Ответ 5

Я просто решил такую проблему, написав простой служебный метод, который проверит всю вызванную цепочку.

  /**
   * Recursive method to determine whether an Exception passed is, or has a cause, that is a
   * subclass or implementation of the Throwable provided.
   *
   * @param caught          The Throwable to check
   * @param isOfOrCausedBy  The Throwable Class to look for
   * @return  true if 'caught' is of type 'isOfOrCausedBy' or has a cause that this applies to.
   */
  private boolean isCausedBy(Throwable caught, Class<? extends Throwable> isOfOrCausedBy) {
    if (caught == null) return false;
    else if (isOfOrCausedBy.isAssignableFrom(caught.getClass())) return true;
    else return isCausedBy(caught.getCause(), isOfOrCausedBy);
  }

Когда вы используете его, вы просто создаете список if от наиболее конкретного исключения до наименее определенного с резервным оператором else:

try {
  // Code to be executed
} catch (Exception e) {
  if (isCausedBy(e, MyException.class)) {
    // Handle MyException.class
  } else if (isCausedBy(e, AnotherException.class)) {
    // Handle AnotherException.class
  } else {
    throw new IllegalStateException("Error at calling service 'service'");
  }
}

Ответ 6

Вы можете сделать следующее:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}

Ответ 7

Я сомневаюсь, но вы можете проверить с помощью instanceof, если исключение имеет правильный тип.

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