【发布时间】:2011-06-08 21:24:15
【问题描述】:
我有一个相当详细的问题,关于包装检查异常的正确方法,以及 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 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,所以它应该真的在一个基础库中我可以包括任何地方。
这让我想到,也许 Guava 在某个地方有一个标准的 WrappedException 类,因为我现在默认将 Guava 作为依赖项。所以我只能这样做
throw new WrappedException(e);
或
throw Exceptions.wrap(e);
或
Exceptions.rethrow(e);
我刚刚在 Guava 中环顾四周,发现 Throwables 与 Throwables.propagate() 看起来相似,但它只是将检查的异常包装在 RuntimeException 中,而不是 RuntimeException 的特殊子类。
哪种方法更好?与RuntimeException 相比,我不应该使用特殊的WrappedException 吗?我的顶级代码想知道增加信息价值的最顶层异常。
如果我有一个 RuntimeException 包装了一个 NastyException 包装了一个 NullPointerException,那么包装 RuntimeException 不会增加信息价值,我不在乎它,所以我会记录错误将是NastyException。
如果我有一个包含NastyException 的IllegalArgumentException,则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's probably useful
else if (re.getMessage() != null)
return re;
// if a runtime exception has no cause, it's certainly
// going to be more useful than null
else if (re.getCause() == null)
return re;
else
return re.getCause();
}
这种理念对我来说是正确的,但实施起来感觉很糟糕。有没有更好的方法来处理包装的异常?
相关问题:
【问题讨论】:
-
除了日志之外,您的顶级处理代码还做什么?当您记录链式异常的堆栈跟踪时,无论如何您都会获得整个跟踪,因此它是否被包装并不重要。
-
如果你只是在记录,无论如何你都会记录一切,如果你在做一些实际的处理,那么只捕获或尝试解开你可以处理的异常。
getCause是您解包所需的全部内容。 -
@artbristol, @trutheality:是的,我想过在 RuntimeException 上调用 logError()。但是我的应用程序中
logError()的另一部分是向用户显示,我确实需要显示最有用的消息而没有任何噪音;他们不会通过异常堆栈来找出问题所在,而是会查看消息,如果他们能自己找出问题,那就太好了,否则我会收到“我遇到了 RuntimeException,我该怎么办?” -
那么将这段代码放在
logError()中提取消息给用户的部分可能是有意义的。