从Java中的finally块返回
我最近惊讶地发现在Java的finally块中可能有一个return语句。
看起来好像很多人认为按照“ 不要在最后条款中回来”中所描述的那样做是件坏事。 稍微深入一点,我也发现“ Java的回归并不总是如此 ”,它显示了最终块中其他typesstream量控制的一些非常可怕的例子。
所以,我的问题是,任何人都可以给我一个例子,在finally块中的返回语句(或其他stream量控制)产生更好/更易读的代码?
你提供的例子是足够的理由,从最后不使用stream量控制。
即使有一个人为的例子,“更好”,考虑一下开发人员,他们必须稍后维护你的代码,而且谁可能不知道这些微妙之处。 那个可怜的开发者甚至可能是你….
我真的很难find几年前由此造成的错误。 代码是这样的:
Object problemMethod() { Object rtn = null; try { rtn = somethingThatThrewAnException(); } finally { doSomeCleanup(); return rtn; } }
发生了什么事是在其他代码中抛出exception。 它正在被somethingThatThrewAnException()
方法捕获并logging和重新生成。 但是这个exception并没有被传播过去, problemMethod()
。 经过长时间的观察,我们终于find了返回的方法。 finally块中的返回方法基本上阻止了try块中发生的exception,即使它没有被捕获也不会传播。
像其他人所说的那样,根据Java规范从finally块返回是合法的,但是这是一件坏事,不应该这样做。
最后,如果你最后使用-Xlint,javac会提示返回。 最初的javac没有发出警告 – 如果代码有问题,它应该编译失败。 不幸的是,向后兼容意味着不可能禁止出人意料的巧妙的愚蠢行为。
例外情况可以从finally块中抛出,但在这种情况下,展现的行为几乎肯定是你想要的。
将控制结构和返回信息添加到最后的{}块只是“正因为可以”滥用其中几乎分散在所有开发语言中的另一个例子。 杰森正确地认为,这很容易成为维修的噩梦 – 反对早期回报的论点更适用于这种“迟到回报”的情况。
最后,块的存在是为了一个目的,让你完全收拾完自己,不pipe前面的代码发生了什么。 原则上这是closures/释放文件指针,数据库连接等,虽然我可以看到它被延伸说要添加定制审计。
任何影响函数返回的地方都应该在try {}块中。 即使你有一种检查外部状态的方法,耗时的操作,然后再次检查该状态,以防万一它失效,你仍然希望在try {}内进行第二次检查 – 如果它终于在里面了{}而长时间的操作失败了,那么你将会不必要地再次检查这个状态。
一个简单的Groovytesting:
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”)并覆盖exception吗?