惯用Java“finally”阻止只在异常上运行

时间:2014-10-29 21:10:16

标签: java

如果您有一些需要运行的清理代码,无论是否抛出异常,try { ... } finally { ... }都是您需要的。但是,如果您希望清理代码仅在发生异常时运行,该怎么办?

一种可能的解决方案是:

boolean ok = false;
try {
    doWork();
    ok = true;
} finally {
    if (!ok) {
        doCleanup();
    }
}

它完全符合我的要求,但我想知道是否有更自然的方式来表达这个想法。

这个替代方案没有通过集合:

try {
    doWork();
} catch (Throwable e) {
    doCleanup();
    throw e; // BAD: Cant do this unless the enclosing function is declared to throw Throwable
}

我想我可以捕获Exception而不是Throwable,但我不喜欢非Exception throwable会绕过清理代码的想法。毕竟,即使对于非Exception throwable,常规finally块仍然会运行。

除此之外:在C ++中,您只需使用 catch-all 块:

try {
    doWork();
} catch (...) {
    doCleanup();
    throw;
}

3 个答案:

答案 0 :(得分:6)

从Java 7开始,这是合法代码:

public static void method() {
  try {
    // stuff which doesn't declare checked exceptions
  } catch (Throwable t) { 
    throw t; 
  }
}

改进的语言规则允许这样做,因为静态分析可以证明t实际上不会是任何已检查的异常。

同样,您只需要声明最小必要的已检查例外:

public static void method() throws IOException {
  try {
    throwingMethod();
  } catch (Throwable t) { throw t; }
}

static void throwingMethod() throws IOException {}

答案 1 :(得分:4)

如果您有try ... finally阻止,则适用以下两种情况之一:

  1. 您在方法上有throws子句,而try块可能会抛出已检查的异常。
  2. 拥有catch阻止RuntimeException和任何已检查例外的阻止:

    catch (RuntimeException | YourCheckedException e) {
        // Cleanup
        throw e;
    }
    
    1. 您没有throws子句,try块只能抛出未经检查的异常。
    2. Catch RuntimeException,不需要在throws中声明。

      catch (RuntimeException e) {
          // Cleanup
          throw e;
      }
      

答案 2 :(得分:1)

  

它完全符合我的要求,但我想知道是否有更自然的方式来表达这个想法。

语言具有独特的语法结构。在Java中,您展示了第一个选项:

boolean ok = false;
try {
    doWork();
    ok = true;
} finally {
    if (!ok) {
        doCleanup();
    }
}

这是惯用的用Java做的方式,感觉自然(无双关语)自然所述语言的语法。

您正在寻找一种自然的on-error only机制,Java没有。您正在尝试创建一个清理机制,而不涉及对已检查的throwable进行编译器检查。有人会想使用泛型,但泛型有限制(即,不能参数化throwable的子类。)

但是,IIRC,有一些方法可以通过使用Class参数化封闭方法来绕过编译器检查,然后通过反射制作实例并抛出它。但是,在一般情况下,现在我们正在进入毛茸茸的领域,没有明显和有用的原因。

我会坚持你所展示的榜样。很清楚它的作用是什么,它是干净的,并且没有asinine和意想不到的陷阱。

.PS。

对于对上帝的爱,我希望你没有使用C ++全能的习惯用法(至少在没有使用一系列特定类型的catch块之前,包括std :: exception)。

没有什么比处理到达这样一个全能块的代码更糟糕了。没有任何奇怪的信息,几乎不可能追踪扔掉它的东西。

去过那里并且不以此为生。噩梦的东西。