Возвращение из блока finally в Java
недавно я был удивлен, обнаружив, что можно иметь оператор return в блоке finally в Java.
похоже, что многие люди думают, что это плохо, как описано в 'не возвращайтесь в предложение finally'. Почесав немного глубже, я тоже нашел'возврат Java не всегда', который показывает некоторые довольно ужасные примеры других видов контроля потока в блоках finally.
Итак, мой вопрос, может кто-нибудь дать мне пример, когда оператор return (или другое управление потоком) в блоке finally создает лучший / более читаемый код?
5 ответов:
приведенные вами примеры являются достаточным основанием для не используйте управление потоком из finally.
даже если есть надуманный пример, где это "лучше", рассмотрим разработчика, который должен поддерживать ваш код позже и который может не знать о тонкостях. Этот бедный разработчик может быть даже вы....
у меня было очень трудное время, чтобы отследить ошибку много лет назад, которая была вызвана этим. Код был что-то вроде:
Object problemMethod() { Object rtn = null; try { rtn = somethingThatThrewAnException(); } finally { doSomeCleanup(); return rtn; } }
произошло то, что исключение было брошено в какой-то другой код. Он был пойман и зарегистрирован и перерос в
somethingThatThrewAnException()
метод. Но исключение не распространялось мимоproblemMethod()
. После долгого рассмотрения этого мы, наконец, отследили его до Метода возврата. Метод возврата в блоке finally в основном останавливался исключение, которое произошло в блоке try от распространения вверх, даже если оно не было поймано.как говорили другие, хотя законно возвращаться из окончательного блока в соответствии со спецификацией Java, это плохо и не должно быть сделано.
javac предупредит о возврате в конце концов, если вы используете-Xlint: finally. Первоначально javac не выдавал никаких предупреждений - если что-то не так с кодом, он не должен компилироваться. К сожалению, обратная совместимость означает, что неожиданная гениальная глупость не может быть запрещена.
исключения могут быть выброшены из блоков finally, но в этом случае показанное поведение почти наверняка то, что вы хотите.
добавление структур управления и возврат к блокам finally {} - это еще один пример злоупотреблений "просто потому, что вы можете", которые разбросаны практически по всем языкам разработки. Джейсон был прав, предполагая, что это может легко стать кошмаром обслуживания - аргументы против ранних возвратов из функций применяются больше-так что к этому случаю "поздних возвратов".
наконец, блоки существуют для одной цели, чтобы вы могли полностью убирать за собой, независимо от того, что произошло во всем предшествующем коде. В основном это закрытие / освобождение указателей на файлы, соединения с базой данных и т. д. хотя я мог видеть, что он растягивается, чтобы сказать добавление в заказном аудите.
все, что влияет на возврат функции, должно находиться в блоке try {}. Даже если у вас есть метод, с помощью которого вы проверили внешнее состояние, выполнили трудоемкую операцию, а затем снова проверили это состояние в случае, если оно стало недействительным, вы все равно хотите вторую проверку внутри попытки{} - если бы он сидел внутри, наконец, {} и длительная операция не удалась, вы бы тогда проверяли это состояние во второй раз без необходимости.
простой Заводной тест:
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.снимать.(.) строка в операторе finally - будет ли это перезаписывать обычное возвращаемое значение ("OK") и покрывать исключение?!