我最近惊讶地发现在Java中,finally
块内可以使用return
语句。
很多人认为这是一件不好的事情,就像在'不要在finally
子句中使用return
'中描述的那样。更深入地了解后,我还发现了'Java的return
并不总是',其中展示了在finally
块中使用其他类型的流控制会产生一些非常可怕的例子。
因此,我的问题是,是否有人能给我提供一个在finally
块中使用return
语句(或其他流控制)可以产生更好/更易读代码的示例?
我最近惊讶地发现在Java中,finally
块内可以使用return
语句。
很多人认为这是一件不好的事情,就像在'不要在finally
子句中使用return
'中描述的那样。更深入地了解后,我还发现了'Java的return
并不总是',其中展示了在finally
块中使用其他类型的流控制会产生一些非常可怕的例子。
因此,我的问题是,是否有人能给我提供一个在finally
块中使用return
语句(或其他流控制)可以产生更好/更易读代码的示例?
多年前我曾经很难追踪一个由此引起的bug。代码大致如下:
Object problemMethod() {
Object rtn = null;
try {
rtn = somethingThatThrewAnException();
}
finally {
doSomeCleanup();
return rtn;
}
}
发生的情况是异常在其他代码中被抛出。它被捕获并记录,并在 somethingThatThrewAnException()
方法中重新抛出。但是异常未能从 problemMethod()
中向上传播。经过长时间的研究,我们最终发现了返回方法的问题。finally 块中的返回方法基本上阻止了 try 块中发生的异常传播,即使它没有被捕获。你提供的例子已经足以说明,不要使用finally中的流程控制。
即使有一个牵强附会的例子可以表明它的“优越性”,也要考虑维护代码的开发人员,他可能不知道其中的微妙之处。而那个可怜的开发人员甚至可能就是你自己....
如果使用-Xlint:finally,javac会警告finally中的return语句。原先,javac不会发出任何警告——如果代码有问题,它应该无法编译。不幸的是,向后兼容性意味着无法禁止未预料到的聪明愚蠢行为。
可以从finally块中抛出异常,但在这种情况下,展示的行为几乎肯定是您想要的。
在finally{}块中添加控制结构和返回语句只是“因为你可以”滥用的又一个例子,这种滥用几乎遍布所有开发语言。Jason提出它很容易成为维护噩梦的观点是正确的-反对从函数中早期返回的论点更适用于这种“晚期返回”的情况。
Finally块存在的唯一目的是允许您彻底整理自己的代码,不管之前的代码发生了什么。主要是关闭/释放文件指针、数据库连接等,虽然我可以看到它被扩展为添加定制审计。
影响函数返回的任何内容都应该位于try{}块中。即使您有一个方法,在其中检查外部状态,并执行耗时操作,然后再次检查该状态以避免其变为无效,您仍然希望将第二个检查放在try{}块中-如果它在finally{}内,则长时间操作失败,然后您将不必要地再次检查该状态。
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中,可以仅仅在方法结尾处留下一个值(不需要return语句)就可以“返回”方法。如果您取消注释finally语句中的runningThreads.remove(..)行,会发生什么 - 它会覆盖常规返回值(“OK”)并覆盖异常吗?!
finally
块中返回将导致 exceptions
被丢失。finally
块中的返回语句将导致在 try 或 catch 块中可能抛出的任何异常被丢弃。注意:根据JLS 14.17,返回语句总是会突然完成。If execution of the try block completes abruptly for any other reason R, then the finally block is executed, and then there is a choice:
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).