Возвращение из блока finally в Java


недавно я был удивлен, обнаружив, что можно иметь оператор return в блоке finally в Java.

похоже, что многие люди думают, что это плохо, как описано в 'не возвращайтесь в предложение finally'. Почесав немного глубже, я тоже нашел'возврат Java не всегда', который показывает некоторые довольно ужасные примеры других видов контроля потока в блоках finally.

Итак, мой вопрос, может кто-нибудь дать мне пример, когда оператор return (или другое управление потоком) в блоке finally создает лучший / более читаемый код?

5 147

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") и покрывать исключение?!