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