У меня есть довольно подробный вопрос о правильном способе обернуть проверенное исключение и то, как это делает Guava. (Извините за длину, но я хочу, чтобы мой мыслительный процесс был опущен)
Стандартный интерфейс Runnable выглядит следующим образом:
public interface Runnable
{
public void run();
}
где run()
не может выставить проверенное исключение.
Итак, если я хочу иметь Runnable
, который используется для обертывания задач, которые выдают проверенные исключения, и я намереваюсь иметь вещь, которая вызывает Runnable.run()
обрабатывать эти исключения, а не в Runnable.run()
, я имею для исключения исключения в исключенном исключении.
Итак, какое-то время я использовал:
Runnable r = new Runnable {
@Override public void run()
{
try {
doNastyStuff();
}
catch (NastyException e)
{
throw new RuntimeException(e);
}
}
};
а затем я могу обработать RuntimeException на верхнем уровне. За исключением того, что я решил, что я действительно хочу обрабатывать исключение wrapped отдельно, так как я знаю, что его семантика заключается в переносе проверенного исключения, поэтому я написал этот вспомогательный класс:
/**
* Wrapped exception: the purpose of this is just to wrap another exception,
* and indicate that it is a wrapped exception
*/
public class WrappedException extends RuntimeException
{
/**
* @param t any throwable
*/
public WrappedException(Throwable t)
{
super(t);
}
}
а затем я могу это сделать:
/* place that produces the exception */
...
catch (NastyException e)
{
throw new WrappedException(e);
}
...
/* upper level code that calls Runnable.run() */
try
{
...
SomeOtherNastyCode();
r.run();
...
}
catch (SomeOtherNastyException e)
{
logError(e);
}
catch (WrappedException e)
{
logError(e.getCause());
}
и, похоже, он отлично работает.
Но теперь я думаю, что если я хочу использовать это в библиотеке, а также приложение, использующее библиотеку, теперь они оба зависят от WrappedException, поэтому он действительно должен быть в базовой библиотеке, которую я могу включая всюду.
Что заставляет меня думать, может быть, у Гуавы есть класс WrappedException где-то, так как теперь я включаю Guava как зависимость по умолчанию. Поэтому я могу просто сделать
throw new WrappedException(e);
или
throw Exceptions.wrap(e);
или
Exceptions.rethrow(e);
Я просто посмотрел в Гуаву и нашел Throwables, у которого Throwables.propagate()
, который похож, но он просто переносит проверенные исключения в RuntimeException
, а не специальный подкласс RuntimeException.
Какой подход лучше? Должен ли я использовать специальное исключение WrappedException по сравнению с RuntimeException? Мой код верхнего уровня хочет знать самое верхнее исключение, которое добавляет информационное значение.
Если у меня есть RuntimeException, которое обертывает исключение NastyException, которое обертывает исключение NullPointerException, обертывание RuntimeException не добавляет информационное значение, и я не забочусь об этом, поэтому ошибка, которую я регистрировал бы, была бы NastyException.
Если у меня есть исключение IllegalArgumentException, которое обертывает NastyException, исключение IllegalArgumentException обычно добавляет информационное значение.
Итак, в моем верхнем коде, который регистрирует ошибки, мне нужно будет сделать что-то вроде этого:
catch (RuntimeException re)
{
logError(getTheOutermostUsefulException(re));
}
/**
* heuristics to tease out whether an exception
* is wrapped just for the heck of it, or whether
* it has informational value
*/
Throwable getTheOutermostUsefulException(RuntimeException re)
{
// subclasses of RuntimeException should be used as is
if (re.getClass() != RuntimeException)
return re;
// if a runtime exception has a message, it probably useful
else if (re.getMessage() != null)
return re;
// if a runtime exception has no cause, it certainly
// going to be more useful than null
else if (re.getCause() == null)
return re;
else
return re.getCause();
}
Философия чувствует себя хорошо для меня, но реализация плохо. Есть ли лучший способ обработки завернутых исключений?
связанные вопросы: