从Java中的finally块返回


176

最近我很惊讶地发现,在Java的finally块中可能有一个return语句。

似乎很多人都认为这是一件坏事,如“ 不要在finally子句中返回”中所述。更深入地研究,我还发现“ Java的回报并不总是 ”,这在finally块中显示了一些其他类型的流控制的可怕例子。

因此,我的问题是,谁能给我一个示例,其中finally块中的return语句(或其他流控制)产生更好/更易读的代码吗?

Answers:


89

您提供的示例足够有理由从头开始使用流控制。

即使有一个“更好”的人为设计示例,也请考虑必须稍后维护您的代码并且可能不知道其细微之处的开发人员。那个可怜的开发人员甚至可能就是你。


5
当然。我想我想问的是,如果有人能给我一些很好的榜样,那就好。
马特·谢泼德

@daos中的@MattSheppard,我经常会在最后
Blake

147

多年前,我很难找到一个由此引起的错误。代码类似于:

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

发生的事情是该异常已在其他一些代码中抛出。该方法被捕获并记录并重新抛出somethingThatThrewAnException()。但是例外并没有被传播过去problemMethod()。经过长时间的研究,我们终于将其归结为return方法。最终块中的return方法基本上是阻止try块中发生的异常传播,即使未被捕获。

就像其他人所说的那样,虽然根据Java规范从finally块返回是合法的,但这是一件不好的事情,不应该这样做。


那应该把收益放在哪里?
parsecer

@parsecer我会在try块中调用somethingThatThrewAnException()之后立即说
Tiago Sippert,

@parsecer,?? 最后之后,只需按通常的方式进行即可。
Pacerier

21

如果使用-Xlint:finally,则javac将最终发出返回警告。最初,javac不会发出警告-如果代码有问题,则应无法编译。不幸的是,向后兼容意味着无法防止意外的愚蠢行为。

可以从finally块中引发异常,但是在那种情况下,所显示的行为几乎可以肯定是您想要的。


13

添加控制结构并返回到finally {}块只是“只是因为您可以”滥用的另一个示例,这些滥用实际上散布在所有开发语言中。杰森(Jason)正确地暗示了它很容易成为维护的噩梦-反对函数提早返回的论点更多,因此适用于这种“迟来返回”的情况。

最终,存在块是出于一个目的,无论您之前的代码发生什么情况,都可以让自己彻底整理一下。原则上讲,这是关闭/释放文件指针,数据库连接等,尽管我可以看到有人说可以添加定制审核。

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


6

一个简单的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”)并覆盖异常吗?


0

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

finally块中的return语句将导致任何在try或catch块中引发的异常都将被丢弃。

根据Java语言规范:

如果由于任何其他原因R导致try块的执行突然完成,则执行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语句总是突然完成。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.