从 Java 中的 finally 块返回
-
09-06-2019 - |
题
最近我很惊讶地发现Java 中的finally 块中可以有return 语句。
似乎很多人都认为这样做是一件坏事,如“不要在finally子句中返回'。再深入一点,我还发现了'Java的返回并不总是' 它显示了finally块中其他类型的流量控制的一些非常可怕的例子。
所以,我的问题是,谁能给我一个例子,其中finally块中的return语句(或其他流程控制)会产生更好/更可读的代码?
解决方案
您提供的示例足以说明 不是 最后使用流量控制。
即使有一个人为的示例“更好”,也要考虑稍后必须维护代码并且可能不知道其中微妙之处的开发人员。那个可怜的开发者甚至可能就是你......
其他提示
几年前我很难找到由此引起的错误。代码是这样的:
Object problemMethod() {
Object rtn = null;
try {
rtn = somethingThatThrewAnException();
}
finally {
doSomeCleanup();
return rtn;
}
}
发生的情况是在其他代码中引发了异常。它被捕获、记录并重新扔进 somethingThatThrewAnException()
方法。但异常并没有传播过去 problemMethod()
. 。经过很长时间的研究,我们终于找到了 return 方法。finally 块中的 return 方法基本上阻止了 try 块中发生的异常向上传播,即使它没有被捕获。
正如其他人所说,虽然根据 Java 规范从 finally 块返回是合法的,但这是一件坏事,不应该这样做。
如果使用 -Xlint:finally,javac 会在 finally 中发出返回警告。最初 javac 没有发出警告 - 如果代码有问题,它应该无法编译。不幸的是,向后兼容性意味着无法禁止意想不到的巧妙愚蠢行为。
finally 块可以抛出异常,但在这种情况下,所表现出的行为几乎肯定是您想要的。
添加控制结构并返回finally{}块只是“因为你可以”滥用的另一个例子,这种滥用几乎遍布所有开发语言。Jason 正确地指出,这很容易成为维护噩梦 - 反对函数提前返回的论点更适用于这种“延迟返回”的情况。
finally 块的存在只有一个目的,那就是让你可以自己彻底清理,无论前面的代码发生了什么。主要是关闭/释放文件指针、数据库连接等,尽管我可以看到它被延伸为添加定制审计。
任何影响函数返回的内容都应该位于 try{} 块中。即使您有一种方法,可以检查外部状态,执行耗时的操作,然后再次检查该状态以防其无效,您仍然需要在 try{} 内进行第二次检查 - 如果它位于 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 中,可以从方法“返回”,只需在末尾留下一个值(不返回)。如果您取消注释,您认为会发生什么 runningThreads.remove(..) 最后语句中的行 - 这会覆盖常规返回值(“OK”)并覆盖异常吗?!