Ява: как ваще козырь кидает?

Я только что наткнулся на скрытую жемчужину в одной из наших библиотек Java:

for(Widget w : widgets) {
    if(shouldDoStuff()) {
        try{
            // Do stuff to w.
        } catch(Exception e){
            throw new RuntimeException("Couldn't do stuff.");
        } finally{
            // Compiler warning: finally block does not complete normally
            continue;
        }
    }
}

Я знаю, что finally превосходит все, но меня интересуют 2 вещи:

  1. Что происходит, когда предложение catch действительно выполняется? Выбрасывается ли исключение или нет? Что происходит первым: выброшенное исключение или инструкция continue?
  2. Как я могу переписать это, чтобы избавиться от предупреждения?

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


person AdjustingForInflation    schedule 28.02.2014    source источник
comment
Что вы имеете в виду под козырями все?   -  person fge    schedule 28.02.2014
comment
Это дубликат stackoverflow.com/questions/5126455/, который хорошо отвечает на вопрос (прочитайте объяснение). В основном исключение поглощается возвратом. В основном делает бросок бесполезным.   -  person M. Deinum    schedule 28.02.2014
comment
Предполагается, что блок finally выполняет завершающие действия, такие как задачи очистки. Он не должен делать ничего, кроме этого. если вы удалите оператор continue, предупреждение не будет отображаться.   -  person stinepike    schedule 28.02.2014
comment
2. если continue; является единственным оператором в блоке finally, просто переместите его из try/catch и отбросьте finally   -  person fge    schedule 28.02.2014
comment
Вот еще один подобный вопрос SO с кучей объяснений: stackoverflow.com/questions/65035/   -  person Daniël Knippers    schedule 28.02.2014


Ответы (3)


Выбрасывается исключение. Если запустить, вы увидите:

  1. RuntimeException("Couldn't do stuff.")
  2. Делайте то, что в finally.

Если бы была внешняя попытка/поймать, которая поймала RuntimeException, то программа могла бы продолжить работу. Оператор continue в блоке finally бесполезен. Единственный раз, когда, наконец, не будет вызвано, это если вы вызовете System.exit() или если сначала произойдет сбой JVM.

person Engineer2021    schedule 28.02.2014
comment
Спасибо @GI Joe (+1). Можете ли вы объяснить, почему оператор continue бесполезен? Я думаю, что автор этого кода хотел, по сути, разветвить выполнение и повторно создать исключение, но при этом продолжить обработку списка Widgets. - person AdjustingForInflation; 28.02.2014
comment
@TotesMcGotes: в этом примере показан анти-шаблон исключения. Это плохой пример. Кроме того, использование finally не требуется в JDK7+. Но обычно он используется для очистки ресурсов. - person Engineer2021; 28.02.2014

Код формы

try {
  // non-control-flow statements A
} finally {
  // non-control-flow statements B
}

компилируется так, как если бы вы написали:

try {
  // non-control-flow statements A
} catch(Throwable t) {
  // non-control-flow statements B
  throw t;
}
// non-control-flow statements B

Так что операторы B выполняются в любом случае, исключительном или неисключительном. Но если вы вставляете оператор потока управления, такой как continue, в блок finally, вы превращаете скрытый оператор re-throw t; в мертвый код.

Есть простой способ добиться того же без предупреждения. Напишите явный обработчик исключений, делающий то же самое, но не содержащий этого неэффективного оператора re-throw. Таким образом вы документируете, что проглатывание исключения является преднамеренным. Тогда предупреждение исчезнет. Однако проглатывание исключений по-прежнему остается плохим стилем кода.

person Holger    schedule 28.02.2014

person    schedule
comment
Оператор continue не принадлежит блоку finally, поэтому компилятор жалуется. (Значок return тоже не подходит.) Кроме того, это правильный ответ; finally запускается перед выходом из блока независимо от того, как он выходит. И нет, я понятия не имею, что они имели в виду под abruptly. - person keshlam; 28.02.2014
comment
Спасибо @Nicolas Defranoux (+1) - пожалуйста, смотрите мой комментарий под ответом GI Joe - у меня к вам тот же вопрос! - person AdjustingForInflation; 28.02.2014
comment
continue пытается продолжить цикл, но, поскольку в catch есть бросок, выполняется finally, а затем возникает исключение выше, и цикл все равно прерывается. - person Nicolas Defranoux; 28.02.2014
comment
Вопрос: что ты хочешь делать? Если вы хотите обработать все элементы в списке даже при возникновении некоторых исключений, удалите бросок из улова. Вы все равно можете сохранить исключение и выбросить его после завершения цикла, но что вы будете делать, если исключений несколько? - person Nicolas Defranoux; 28.02.2014
comment
Почему бы не запомнить фактическое исключение, а не boolean? Затем вы можете повторно выдать настоящее исключение вместо неспецифического RuntimeException. - person Holger; 28.02.2014
comment
Конечно, это сработает... но что произойдет, если возникнут два исключения? Вам нужен список исключений, а затем вы можете создать специальное исключение, которое обрабатывает несколько исключений, чтобы вы могли генерировать их все сразу. - person Nicolas Defranoux; 01.03.2014