2009-10-30 51 views
11

后期编辑:这个问题将有望被淘汰时的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通常是皱眉,我不是很喜欢用反射来重新抛出一个异常,而且我总是感到有点不自在写“这不会发生”的评论,但实际上它的工作原理是w埃尔(或者似乎至少)。我想知道的是:

  1. 我的rethrow帮助器方法有任何缺陷吗?我错过了一些角落案件? (我知道Throwable可能是由一些如此严重的东西造成的,以至于我的undoInitialize会失败,但没关系。)
    • 有人已经发明了这个吗?我看着Commons Lang的ExceptionUtils,但那是做其他事情的。

编辑:

  • finally是不是我要找的机器人。我只有在抛出异常时才有兴趣做些什么。
  • 是的,我知道追赶Throwable是一大禁忌,但我认为这是两害取其轻这里相比具有三个catch子句(对于Error,分别RuntimeExceptionSomeException)具有相同的代码。
  • 请注意,我并没有试图压制任何错误 - 想法是try块中抛出的任何异常会在我重新执行一些操作时立即通过调用堆栈。
+1

你有抓住Throwable的好理由。不要让它让你失望。这很像使用GOTO,“除非必须,否则不要使用它。” –

回答

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); 
} 

停止(的Throwable t)的方法不会停止线程,相反,它会导致线程扔在unchecked方式提供的除外。

您可以使用Unsafe.throwException()稍微摆弄一下,并且有一种方法可以用我已经忘记的泛型来做到这一点。

1

如果您担心要让您的初始化发生,那么您可能只想将该代码放入finally块中,因为如果应该在某个时刻调用该代码,那么您可能应该始终清理。

作为我想处理的一些异常,我暂时不知道该如何处理,还有一些我只是记录,因为没有用处传递用户无法做任何事情的异常,例如NullPointerException

但是,你没有表现出什么SomeException被定义为,但如果OutOfMemoryException被抛出,你抛出将抓住它,但它可能不会像这样将您的样品功能需要SomeException您的包装同类型,至少当我看看instanceOrUnchecked方法。

您可能希望编写单元测试,尝试不同类别的例外,并查看预期的工作或不工作,以便记录预期的行为。

+0

'SomeException扩展异常'。至于'finally'中的初始化 - 我只希望在引发异常时发生未初始化。我当然可以有一个布尔标志初始设置为false,然后将其设置为true作为try块中的最后一件事,但这有点麻烦(以及如果某人在将代码设置为true之后添加代码而没有考虑异常处理?)。 – gustafc

+0

此外,我测试了它,它工作得很好......在所有情况下,我都能够想到,这只能证明我已经设法编写代码,我不够聪明,无法打破自己=) – gustafc

+1

您是否测试过扩展Throwable的类,而不是扩展了Exception的类? –

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语句后返回,但它仍然抛出异常,如果客户忘记在调用之前把扔到工厂的方法。

这对你的代码的缺点是它的可移植性较差(适用于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; 
     } 
+0

工厂函数很好,但由于我有一个异常层次结构(几个类扩展'SomeException'),我需要更灵活的东西。我喜欢第二种选择的聪明之处,但我认为它可能太聪明了 - 维护程序员的WTF潜力似乎很高。 – gustafc