最近我感到惊讶的是,在Java的最后一个块中有一个返回语句是可能的。
似乎很多人都认为这是一件糟糕的事情,如"不要在最后一句中返回"所述。抓取更深一点,我还发现"Java的返回并不总是",这显示了一些非常可怕的例子,其他类型的流控制在最后块。
所以,我的问题是,有人能给我一个例子,在这个例子中,finally块中的返回语句(或其他流控制)产生更好/更可读的代码吗?
几年前,我很难找到由这个原因引起的错误。代码是这样的:
1 2 3 4 5 6 7 8 9 10
| Object problemMethod() {
Object rtn = null;
try {
rtn = somethingThatThrewAnException();
}
finally {
doSomeCleanup();
return rtn;
}
} |
发生的是在其他代码中抛出了异常。它在somethingThatThrewAnException()方法中被捕获、记录和重新拥有。但是这个例外并没有传播到problemMethod()上。经过长时间的研究,我们终于找到了返回方法。finally块中的返回方法基本上阻止了try块中发生的异常传播,即使没有捕获到它。
正如其他人所说的,虽然根据Java规范从最终块返回是合法的,但这是一件坏事,不应该做。
您提供的示例是不使用finally中的流控制的充分理由。
即使有一个"更好"的人为例子,也要考虑开发人员,他们必须在以后维护您的代码,并且可能不知道这些细微之处。那个可怜的开发者可能就是你……
如果您使用-xlint:finally,javac将发出返回finally的警告。最初,javac没有发出警告——如果代码有问题,它应该无法编译。不幸的是,向后兼容意味着不能禁止意外的、巧妙的、愚蠢的行为。
可以从finally块抛出异常,但在这种情况下,所展示的行为几乎是您想要的。
向finally块添加控制结构和返回只是"仅仅因为您可以"滥用的另一个例子,这些滥用几乎散布在所有开发语言中。Jason认为这很容易成为维护的噩梦,这是正确的——反对函数早期返回的论点更适用于"延迟返回"的情况。
最后,块的存在有一个目的,允许您在自己之后完全整理,不管前面所有代码中发生了什么。主要是关闭/释放文件指针、数据库连接等,尽管我可以看到它被扩展到说添加定制审计。
影响函数返回的任何内容都应位于try块中。即使您有一个方法检查一个外部状态,执行一个耗时的操作,然后再次检查该状态,以防它变为无效,您仍然希望在try中进行第二次检查-如果它最终位于中,并且长时间操作失败,那么您将不必要地再次检查该状态。
一个简单的groovy测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| public class Instance {
List<String> runningThreads = new ArrayList<String>()
void test(boolean returnInFinally) {
println"
test(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"--------------------------------------------------------------------------
"
}
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) |
输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| 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")并覆盖异常吗?!