关于异常:从Java中的finally块返回

Returning from a finally block in Java

最近我感到惊讶的是,在Java的最后一个块中有一个返回语句是可能的。

似乎很多人都认为这是一件糟糕的事情,如"不要在最后一句中返回"所述。抓取更深一点,我还发现"Java的返回并不总是",这显示了一些非常可怕的例子,其他类型的流控制在最后块。

所以,我的问题是,有人能给我一个例子,在这个例子中,finally块中的返回语句(或其他流控制)产生更好/更可读的代码吗?


几年前,我很难找到由这个原因引起的错误。代码是这样的:

1
2
3
4
5
6
7
8
9
10
Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

发生的是在其他代码中抛出了异常。它在somethingThatThrewAnException()方法中被捕获、记录和重新拥有。但是这个例外并没有传播到problemMethod()上。经过长时间的研究,我们终于找到了返回方法。finally块中的返回方法基本上阻止了try块中发生的异常传播,即使没有捕获到它。

正如其他人所说的,虽然根据Java规范从最终块返回是合法的,但这是一件坏事,不应该做。


您提供的示例是不使用finally中的流控制的充分理由。

即使有一个"更好"的人为例子,也要考虑开发人员,他们必须在以后维护您的代码,并且可能不知道这些细微之处。那个可怜的开发者可能就是你……


如果您使用-xlint:finally,javac将发出返回finally的警告。最初,javac没有发出警告——如果代码有问题,它应该无法编译。不幸的是,向后兼容意味着不能禁止意外的、巧妙的、愚蠢的行为。

可以从finally块抛出异常,但在这种情况下,所展示的行为几乎是您想要的。


向finally块添加控制结构和返回只是"仅仅因为您可以"滥用的另一个例子,这些滥用几乎散布在所有开发语言中。Jason认为这很容易成为维护的噩梦,这是正确的——反对函数早期返回的论点更适用于"延迟返回"的情况。

最后,块的存在有一个目的,允许您在自己之后完全整理,不管前面所有代码中发生了什么。主要是关闭/释放文件指针、数据库连接等,尽管我可以看到它被扩展到说添加定制审计。

影响函数返回的任何内容都应位于try块中。即使您有一个方法检查一个外部状态,执行一个耗时的操作,然后再次检查该状态,以防它变为无效,您仍然希望在try中进行第二次检查-如果它最终位于中,并且长时间操作失败,那么您将不必要地再次检查该状态。


一个简单的groovy测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class Instance {

  List<String> runningThreads = new ArrayList<String>()

  void test(boolean returnInFinally) {

    println"
test(returnInFinally: $returnInFinally)"

    println"--------------------------------------------------------------------------"
    println"before execute"
    String result = execute(returnInFinally, false)
    println"after execute -> result:" + result
    println"--------------------------------------------------------------------------"

    println"before execute"
    try {
      result = execute(returnInFinally, true)
      println"after execute -> result:" + result
    } catch (Exception ex) {
      println"execute threw exception:" + ex.getMessage()
    }  
    println"--------------------------------------------------------------------------
"


  }

  String execute(boolean returnInFinally, boolean throwError) {
      String thread = Thread.currentThread().getName()
      println"...execute(returnInFinally: $returnInFinally, throwError: $throwError) - thread: $thread"
      runningThreads.add(thread)
      try {
        if (throwError) {
          println"...error in execute, throw exception"
          throw new Exception("as you liked :-)")
        }
        println"...return 'OK' from execute"
        return"OK"
      } finally {
        println"...pass finally block"
        if (returnInFinally) return"return value from FINALLY ^^"
        // runningThreads.remove(thread)
      }
  }
}

Instance instance = new Instance()
instance.test(false)
instance.test(true)

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
test(returnInFinally: false)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: OK
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: false, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
execute threw exception: as you liked :-)
-----------------------------------------------------------------------------


test(returnInFinally: true)
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: false) - thread: Thread-116
...return 'OK' from execute
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------
before execute
...execute(returnInFinally: true, throwError: true) - thread: Thread-116
...error in execute, throw exception
...pass finally block
after execute -> result: return value from FINALLY ^^
-----------------------------------------------------------------------------

问题:

对我来说,一个有趣的地方是看看groovy是如何处理隐式回报的。在groovy中,只需在末尾留下一个值(不返回),就可以从方法"返回"。如果取消对finally语句中runningthreads.remove(..)行的注释,您认为会发生什么?这会覆盖常规返回值("ok")并覆盖异常吗?!