Возвращаясь из блока finally в Java - PullRequest
162 голосов
/ 07 сентября 2008

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

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

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

Ответы [ 5 ]

133 голосов
/ 08 сентября 2008

У меня было ОЧЕНЬ трудное время, чтобы отследить ошибку, которая была вызвана этим. Код был что-то вроде:

Object problemMethod() {
    Object rtn = null;
    try {
        rtn = somethingThatThrewAnException();
    }
    finally {
        doSomeCleanup();
        return rtn;
    }
}

Произошло то, что исключение было сгенерировано в другом коде. Он был пойман и зарегистрирован и переброшен в рамках метода somethingThatThrewAnException(). Но исключение не распространялось выше problemMethod(). После долгого времени просмотра мы наконец отследили его до метода return. Метод return в блоке finally в основном останавливал распространение исключения, которое произошло в блоке try, даже если оно не было перехвачено.

Как уже говорили другие, хотя законно возвращаться из блока finally в соответствии со спецификацией Java, это ПЛОХАЯ вещь, и делать это не следует.

84 голосов
/ 07 сентября 2008

Приведенные вами примеры являются достаточной причиной, чтобы не использовал управление потоком от finally.

Даже если есть надуманный пример, где «лучше», подумайте о разработчике, который должен поддерживать ваш код позже и который может не знать о тонкостях. Этот бедный разработчик может быть даже вы ....

19 голосов
/ 07 сентября 2008

javac предупредит о возврате в конце, если вы используете -Xlint: наконец. Первоначально javac не выдавал предупреждений - если что-то не так с кодом, он не должен компилироваться. К сожалению, обратная совместимость означает, что непредвиденная глупость не может быть запрещена.

Исключения могут быть сгенерированы из блоков finally, но в этом случае демонстрируемое поведение почти наверняка соответствует желаемому.

13 голосов
/ 07 сентября 2008

Добавление управляющих структур и возврата к блокам finally {} - это еще один пример злоупотреблений «просто потому, что вы можете», которые разбросаны практически по всем языкам разработки. Джейсон был прав, предполагая, что он легко может стать кошмаром обслуживания - аргументы против раннего возврата из функций более применимы к этому случаю «позднего возврата».

Наконец, существуют блоки для одной цели, чтобы вы могли полностью привести себя в порядок, независимо от того, что произошло во всем предыдущем коде. В основном это закрытие / освобождение файловых указателей, соединений с базой данных и т. Д., Хотя я мог видеть, что это растягивается, если говорить о добавлении в заказной аудит.

Все, что влияет на возвращение функции, должно находиться в блоке try {}. Даже если у вас есть метод, с помощью которого вы проверяете внешнее состояние, выполняете трудоемкую операцию, а затем снова проверяете это состояние в случае, если оно становится недействительным, вы все равно хотели бы провести вторую проверку внутри попытки {} - если она находится внутри, наконец, {} и длительная операция завершилась неудачно, тогда вы будете без необходимости проверять это состояние во второй раз.

6 голосов
/ 04 марта 2011

Простой 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 - это перезапишет обычное возвращаемое значение («ОК») и охватит исключение?!

...