Возврат из блока 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 - будет ли это перезаписывать регулярное возвращаемое значение ( "ОК" ) и охватывать исключение?!