从 Java 中的 finally 块返回

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

似乎很多人都认为这样做不好,就像“ 不要在 finally 子句中返回”中描述的那样。稍微深入一点,我还发现了“ Java 的回报并不总是”,它显示了其他类型的流控制在 finally 块中的一些非常糟糕的例子。

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

67859 次浏览

您提供的示例足以支持 没有使用 finally 中的流控制。

即使有一个“更好”的人为例子,考虑一下那些以后必须维护您的代码的开发人员,他们可能没有意识到其中的微妙之处。那个可怜的开发商可能就是你。

向 finally {}块添加控制结构和返回值只是“仅仅因为你可以”滥用的另一个例子,这种滥用遍布几乎所有的开发语言。Jason 认为它很容易成为一个维护噩梦(反对函数提前返回的观点更适用于这种情况) ,这是正确的,因此这种“延迟返回”的情况也是如此。

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

影响函数返回的任何内容都应该位于 try {}块中。即使你有一个方法,你检查一个外部状态,做了一个耗时的操作,然后再次检查该状态,以防它变得无效,你仍然需要在 try {}中进行第二次检查——如果它位于 finally {}中,而长操作失败了,那么你将不必要地再次检查该状态。

如果使用-Xlint: finally,javac 将在 finally 中警告返回。最初 javac 没有发出任何警告-如果代码有问题,它应该无法编译。不幸的是,向后兼容性意味着意想不到的巧妙愚蠢行为不能被禁止。

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

几年前,我费了很大劲才找到了一个由此引起的窃听器。代码是这样的:

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

发生的情况是在其他代码中抛出了异常。它在 somethingThatThrewAnException()方法中被捕获、记录和重新抛出。但是异常并没有传播到 problemMethod()之后。经过长时间的研究,我们最终找到了 return 方法。Finally 块中的 return 方法基本上阻止了 try 块中发生的异常向上传播,即使它没有被捕获。

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

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

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

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

根据 < strong > Java 语言规格:

如果 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 语句总是突然完成。