Захват исключения, вложенного в другое исключение
Я хочу поймать исключение, которое вложено в другое исключение. В настоящее время я делаю это так:
} 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 причинам:
-
Вы решили, что детали исходного исключения вряд ли будут полезны для восстановления ошибок приложения... но вы хотите сохранить их для диагностических целей.
-
Вы реализуете методы API, которые не допускают определенного проверенного исключения, но ваш код неизбежно вызывает это исключение. Обычный обходной путь - "провозить" проверенное исключение внутри непроверенного исключения.
-
Вы ленитесь и превращаете разнообразный набор несвязанных исключений в одно исключение, чтобы избежать множества проверенных исключений в сигнатуре вашего метода 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("...");
}
}
Ответ 3
Метод ExceptionUtils # getRootCause() может оказаться очень удобным в таких ситуациях.
Ответ 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
, если исключение имеет правильный тип.
Изменить: должна быть причина, по которой вложенное исключение завернуто, поэтому вы должны спросить себя, в чем цель ловить вложенную.