(延迟编辑:这个问题有望在Java 7出现时过时,因为"final rethrow" feature seems like it will be added。
很多时候,我发现自己处于这样的情况:
do some initialization try { do some work } catch any exception { undo initialization rethrow exception }
在C#中你可以这样做:
InitializeStuff();
try
{
DoSomeWork();
}
catch
{
UndoInitialize();
throw;
}
对于Java来说,没有好的替代品,而且从the proposal for improved exception handling was cut from Java 7开始,看起来好几年才能得到类似的东西。因此,我决定自己动手:
(编辑:半年后,final rethrow is back,或者看起来如此。)
public final class Rethrow {
private Rethrow() { throw new AssertionError("uninstantiable"); }
/** Rethrows t if it is an unchecked exception. */
public static void unchecked(Throwable t) {
if (t instanceof Error)
throw (Error) t;
if (t instanceof RuntimeException)
throw (RuntimeException) t;
}
/** Rethrows t if it is an unchecked exception or an instance of E. */
public static <E extends Exception> void instanceOrUnchecked(
Class<E> exceptionClass, Throwable t) throws E, Error,
RuntimeException {
Rethrow.unchecked(t);
if (exceptionClass.isInstance(t))
throw exceptionClass.cast(t);
}
}
典型用法:
public void doStuff() throws SomeException {
initializeStuff();
try {
doSomeWork();
} catch (Throwable t) {
undoInitialize();
Rethrow.instanceOrUnchecked(SomeException.class, t);
// We shouldn't get past the above line as only unchecked or
// SomeException exceptions are thrown in the try block, but
// we don't want to risk swallowing an error, so:
throw new SomeException("Unexpected exception", t);
}
private void doSomeWork() throws SomeException { ... }
}
这有点罗嗦,抓住Throwable
通常不赞成,我真的很高兴使用反思只是为了重新抛出异常,我总是觉得有点不安写“这不会发生”的评论,但在实践中它运作良好(或似乎至少)。我想知道的是:
Throwable
可能是由于某种严重的问题导致我的undoInitialize
失败,但那没关系。)
ExceptionUtils
,但还有其他的事情。修改
finally
不是我正在寻找的机器人。我只对在抛出异常时做的事情感兴趣。Throwable
是一个很大的禁忌,但我认为这里有较小的邪恶,而不是有三个捕获条款(Error
,RuntimeException
和{{ 1}},分别使用相同的代码。SomeException
块中抛出的任何异常都将继续通过调用堆栈冒出来。 答案 0 :(得分:5)
有两种方法可以解决这个问题。如果您不需要知道异常是什么,首先是我的偏好。
boolean okay = false;
try {
// do some work which might throw an exception
okay = true;
} finally {
if (!okay) // do some clean up.
}
在某些情况下,您可以在没有额外变量的情况下执行相同的操作,具体取决于try块的功能。
第二种选择是黑客,但也有效。
try {
// do some work which might throw an exception
} catch (Throwable t) {
// do something with t.
Thread.currentThread().stop(t);
}
stop(Throwable t)方法不会停止线程,而是导致线程以未经检查的方式抛出异常。
你可以使用Unsafe.throwException()进行一些摆弄,并且有一种方法可以使用我遗忘的泛型来做到这一点。
答案 1 :(得分:1)
如果您担心会发生未初始化,那么您可能只想将该代码放入finally块中,因为如果应该在某个时刻调用它,那么您可能应该总是清理它。
我很想抓住Throwable
作为我想要处理的一些例外,有些我只是记录,因为没有使用传递用户无法做任何事情的例外,例如NullPointerException
。
但是,你没有显示SomeException
被定义为什么,但如果抛出OutOfMemoryException
,你的throwable会捕获它,但它可能与{{1}不同因此,至少在我查看SomeException
方法时,您的示例函数中将需要您的包装器。
您可能希望编写单元测试,尝试不同类别的异常,并查看哪些功能有效或无效,因此您可以记录预期的行为。
答案 2 :(得分:1)
另一种方法是让工厂只在原因是经过检查的异常时创建SomeException:
public static SomeException throwException(String message, Throwable cause) throws SomeException {
unchecked(cause); //calls the method you defined in the question.
throw new SomeException(message, cause);
}
我在方法中输入返回值的原因是客户端可以这样做:
catch (Throwable e) {
undoInitialize();
throw SomeException.throwException("message", e);
}
如果方法具有返回类型,编译器被假装成不需要在catch语句之后返回,但如果客户端忘记在调用factory方法之前放置throw,它仍会抛出异常。
这对你的代码的缺点是它不太可移植(它适用于SomeException,但不适用于SomeOtherException),但这可能没问题,因为它不适用于你需要拥有的每个异常类型撤消初始化。
如果它适合您的用例,您可以将未经检查的调用放在SomeException的构造函数中,并将逻辑提供给所有子类,但这必须适合您的特定项目 - 在一般情况下这不是一个好主意因为它会阻止包装运行时异常。
public SomeException(message, cause) {
super(message, unchecked(cause));
}
private static Throwable unchecked(Throwable cause) {
if (cause instanceof Error) throw (Error) cause;
if (cause instanceof RuntimeException) throw (RuntimeException) cause;
return cause;
}