Перехват исключения, вложенного в другое исключение - PullRequest
26 голосов
/ 02 июня 2010

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

} 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'");
    }
}

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

Ответы [ 7 ]

26 голосов
/ 02 июня 2010

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

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

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

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

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

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

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

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

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


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

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

19 голосов
/ 16 сентября 2011

Метод ExceptionUtils # getRootCause () может оказаться очень полезным в таких ситуациях.

18 голосов
/ 02 июня 2010

Вы должны добавить несколько проверок, чтобы увидеть, действительно ли 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("...");
    }
}
3 голосов
/ 19 июля 2018

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

  /**
   * 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'");
  }
}
2 голосов
/ 02 июня 2010

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

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

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

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

1 голос
/ 06 мая 2016

Вы можете сделать, как показано ниже:

catch (RemoteAccessException e) {
    int index = ExceptionUtils.indexOfThrowable(e, MyExcetption.class)
    if (index != -1) {
         //handleMyException
    } else {
    }
}
0 голосов
/ 02 июня 2010

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...