假设我正在处理FooException
和BarException
。我们假设它们都是未经检查的例外。
我想在堆栈跟踪中看到的是:
com.bar.BarException: Bar Message
at com.baz.BazCode(BazCode.java:123)
...
Caused by: com.foo.FooException: Foo Message
at com.baz.BazCode(BazCode.java:321)
....
Caused by: ...
但是,默认情况下,FooException
的所有记录都将从堆栈跟踪中删除。例如:
// In a class written by me
/**
* ...
* @throws FooException if foo happens
* @throws BarException if bar happens
*/
public void upperFrame() {
try {
foo.doSomething();
} catch (FooException foo) {
bar.doSomethingElse();
}
}
// In class Bar (not written by me)
public void doSomethingElse() {
if (someConditionWhichHappensToBeTrueInThisScenario()) {
throw new BarException("Hello Bar World"); // At this point, FooException gets erased from the stack trace
}
}
如果BarException
有一个(message, cause)
构造函数,那么我可以按照一种相当粗略的“手动克隆”过程来实现我的目标:
try {
foo.doSomething();
} catch (FooException foo) {
try {
bar.doSomethingElse();
} catch (BarException bar) {
BarException bar2 = new BarException(bar.getMessage(), foo);
bar2.setStackTrace(bar.getStackTrace());
throw bar2;
}
}
但是,如果BarException
没有这样的构造函数(例如ClassCastException
),那么我可以做到这样的事情:
try {
foo.doSomething();
} catch (FooException foo) {
try {
bar.doSomethingElse();
} catch (BarException bar) {
RuntimeException e = new RuntimeException("com.bar.BarException: " + bar.getMessage(), foo);
e.setStackTrace(bar.getStackTrace());
throw e;
}
}
这很危险,因为e
的类型错误,因此较高的帧可能无法正确处理。
是否有“最佳实践”方式来处理这种情况?
答案 0 :(得分:4)
一种解决方案是使用Throwable#initCause(Throwable)
方法:
bar.initCause(foo);
答案 1 :(得分:1)
只要将原始异常作为参数传递给新的异常,它就会创建“由...引起”链并保留堆栈跟踪。你的用例似乎有点奇怪。对我来说,恢复或处理错误超过一些日志记录的异常是另一个错误,而不是真正“由”另一个错误引起的。我只需记录“foo”并抛出“bar”。
在某些情况下,我猜你的方式可能有意义。要做到这一点,你可以在doSomethingElse(foo)上传递“foo”,并在遇到问题时抛出新的BarException(foo)。几乎所有标准异常都支持这个构造函数,如果你需要自己创建一个只委托给它们的构造函数。
我个人不会以你的方式使用它们。如果我出于某种原因需要不同的类型,我会使用它们来引发与我捕获的异常类型不同的异常类型。例如,要将特定类型的异常转换为我的应用程序,或者在有意义的情况下将checked转换为unchecked。在这种情况下,保持原始异常和完整的“由...引起”跟踪仍然是好的。