从Java中的finally块返回

gudnpqoy  于 12个月前  发布在  Java
关注(0)|答案(6)|浏览(115)

最近我很惊讶地发现,在Java的finally块中可以有一个return语句。
看起来很多人认为这是一件坏事,就像'Don't return in a finally clause'中描述的那样。更深入一点,我还发现了'Java's return doesn't always',它展示了一些在finally块中的其他类型的流控制的非常可怕的例子。
所以,我的问题是,有没有人给予我一个例子,在finally块中的return语句(或其他流控制)可以产生更好/更可读的代码?

6rvt4ljy

6rvt4ljy1#

几年前,我很难追踪到一个由此引起的bug。代码如下:

Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

字符串
发生的情况是,异常在其他代码中被抛出。它被捕获并记录在somethingThatThrewAnException()方法中并重新抛出。但异常并没有传播到problemMethod()。经过很长时间的观察,我们最终追踪到了return方法。finally块中的return方法基本上是阻止try块中发生的异常,即使没有被发现也会继续传播
就像其他人说的,虽然根据Java规范,从finally块返回是法律的,但这是一件坏事,不应该这样做。

vbopmzt1

vbopmzt12#

您提供的示例足以让not使用finally的流控制。
即使有一个人为的例子,它是“更好”,考虑开发人员谁必须维护您的代码后,谁可能不知道的微妙之处。

myss37ts

myss37ts3#

如果你使用-Xlint:finally,javac会在finally返回时发出警告。最初,javac不会发出警告-如果代码有问题,它应该无法编译。不幸的是,向后兼容意味着无法禁止意想不到的巧妙愚蠢。
可以从finally块中抛出闭包,但在这种情况下,所展示的行为几乎肯定是您想要的。

siotufzp

siotufzp4#

在finally{}块中添加控制结构和返回值只是另一个例子,“仅仅因为你可以”滥用,几乎遍布所有开发语言。Jason说得对,这很容易成为一场维护噩梦-反对函数提前返回值的论点更适用于这种“延迟返回”的情况。
最后,块的存在有一个目的,就是让你可以完全整理自己的代码,不管前面的代码发生了什么。主要是关闭/释放文件指针,数据库连接等,尽管我可以看到它被延伸到说添加定制审计。
任何影响函数返回的东西都应该在try{}块中。即使你有一个方法,可以用来检查一个外部状态,做一个耗时的操作,然后再次检查这个状态以防它变得无效,你仍然需要在try{}中进行第二次检查-如果它位于finally{}中,并且长操作失败,那么你将不必要地第二次检查这个状态。

vptzau2j

vptzau2j5#

一个简单的Groovy测试:

public class Instance {

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

  void test(boolean returnInFinally) {

    println "\ntest(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 "--------------------------------------------------------------------------\n"

  }

  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)

字符串

输出:

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”)并覆盖异常吗?!

bpsygsoo

bpsygsoo6#

finally块内部返回将导致exceptions丢失。

finally块中的return语句将导致try或catch块中可能抛出的任何异常被丢弃。

根据Java语言规范:
如果try块的执行由于任何其他原因R突然完成,那么finally块被执行,然后有一个选择:

If the finally block completes normally, then the try statement
   completes  abruptly for reason R.

   If the finally block completes abruptly for reason S, then the try
   statement  completes abruptly for reason S (and reason R is
   discarded).

字符串

  • 注意:根据JLS 14.17 -return语句总是突然完成。

相关问题