Несколько операторов возврата без ошибки компилятора

Это был вопрос интервью:

public class Demo {

    public static void main(String[] args) {
        System.out.println(foo());
    }

    static String foo() {
        try {
            return "try ...";
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

Мой вопрос в том, почему нет ошибок времени компиляции. Когда у меня есть оператор return в моем блоке finally, он обязан вернуться из finally вместо try и catch. Я попытался скомпилировать этот код с опцией -Xlint, он дает предупреждение как.

warning: [finally] finally clause cannot complete normally

Ответ 1

Он не дает ошибку компиляции, потому что это разрешено Спецификацией языка Java. Тем не менее, он дает предупреждение, потому что включение оператора return в блок finally обычно плохое.

Что происходит в вашем примере, следующее. Выполняется инструкция return в блоке try. Тем не менее, блок finally должен всегда выполняться так, чтобы он выполнялся после завершения блока catch. Оператор return, встречающийся там, перезаписывает результат предыдущего оператора return, и поэтому метод возвращает второй результат.

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

Ответ 2

Это описано в Спецификации языка Java:

§14.17

Резкое завершение предложения finally может нарушить передачу управление, инициированное оператором return.

§14.20.2

Если выполнение блока try завершается нормально, то finallyблок выполняется, а затем есть выбор:

  • Если блок finally завершается нормально, то инструкция try завершается нормально.
  • Если блок finally неожиданно завершает работу по причине S, то оператор try завершается внезапно по причине S.

Если выполнение блока try завершается внезапно по любой другой причине R, то выполняется блок finally, а затем выбор:

  • Если блок finally завершается нормально, то оператор try завершается внезапно по причине R.
  • Если блок finally неожиданно завершает работу по причине S, то оператор try завершается внезапно по причине S (и причина R отбрасывается).

Ответ 3

Ошибка компиляции отсутствует, поскольку только 1 и точно 1 из оператора return вернут управление обратно в вызывающий код.

Как объясняется @Hoopje, return внутри try или catch будет выполняться первым, их соответствующий оператор возврата также выполнит. Но перед возвратом управления обратно к вызывающему коду он выполнит блок finally. Теперь этот block также return что-то, поэтому этот возврат переопределяет предыдущий.

Ответ 4

Блестящий вопрос.. По моим сведениям, возврат команды try и catch передается, наконец, если вы добавили окончательный блок в свой код. Это как его работа.

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

public class Main {

    public static void main(String[] args) {
        System.out.println(foo());
    }
    static String foo() {
        try {
            throw new Exception();
        } catch (Exception e) {
            return "catch ...";
        } finally {
            return "finally ..."; //got as result
        }
    }
}

Вы можете получить эту идею из-под ссылки. Несколько возвратов: какой из них задает окончательное возвращаемое значение?

Ответ 5

Это по существу то же самое, что и:

public boolean someMethod(){
        if(1 == 1){
            return true;
        }
        return false;
}

Он не даст ошибку компиляции, хотя и даст предупреждение. Компилятор будет выдавать ошибку только в случае выполнения no оператора return.

Ответ 6

Ваш код работает отлично, потому что в try, catch и finally блокируется только один оператор return. Ошибка компиляции произойдет, если вы попытаетесь написать два оператора return внутри одного из блоков try, catch или finally, говорящих, что существует недостижимый оператор return.

Ответ 7

(Для краткого ответа - прочитайте жирные и курсивные части ответа)

Выполняется поток в соответствии с документами Java 8. Он предоставляет вам подробную информацию. Вы можете вывести выполнение операторов возврата на основе следующего.

Оператор try с блоком finally выполняется первым выполнением блока try.

Тогда существует выбор:

• Если выполнение блока try завершается нормально, тогда блок finally выполняется, а затем есть выбор:

- Если блок finally завершается нормально, то инструкция try завершается в нормальном режиме.

- Если блок finally завершается внезапно для разума S, то оператор try завершается внезапно по причине S.

• Если выполнение блока try завершается внезапно из-за выброса значения V, то есть выбор:

- Если тип времени выполнения V соответствует назначению, совместимому с catchable exception класс любого предложения catch инструкции try, тогда первый (самый левый) такой catch. Значение V присваивается параметру выбранное предложение catch и выполняется блок этого условия catch.

Тогда существует выбор:

> Если блок catch завершается нормально, выполняется блок finally. Тогда есть выбор:

"Если окончательный блок завершается нормально, то оператор try нормально завершается.

"Если блок finally окончательно завершается по любой причине, попробуйте Заявление завершается внезапно по той же причине.

> Если блок catch завершается внезапно для причины R, тогда блок finally выполняется. Тогда есть выбор:

"Если окончательный блок завершается нормально, то оператор try завершается внезапно по причине R.

" Если блок finally окончательно завершается по причине S, тогда попытка оператор завершает внезапно по причине S (и причина R отбрасывается).

- Если тип времени выполнения V не присваивается совместимым с уловимым класс исключения любого предложения catch в заявлении try, тогда, наконец, блок выполняется.

Тогда существует выбор:

> Если окончательный блок завершается нормально, то инструкция try завершается внезапно из-за броска значения V.

> Если блок finally окончательно завершается по причине S, то оператор try завершается внезапно по причине S (а выброс значения V отбрасывается и забыли).

• Если выполнение блока try завершается внезапно по любой другой причине R, то наконец, выполняется блок, а затем есть выбор:

- Если блок finally завершается нормально, то инструкция try завершается внезапно по причине R.

- Если блок finally завершается внезапно для разума S, то оператор try завершается внезапно по причине S (и причина R отбрасывается).

поясняется в этой ссылке: javaDoc

Ответ 8

попробуйте выполнить следующее:

он напечатает: 1, 2, 3, а затем сбросит деление на ноль. Исключение

public class Demo {
  public static void main(String[] args) {
    System.out.println(foo());
  }
  public static String print(int a){
    System.out.println(a);
    return String.valueOf(a/0);
  }
  static String foo() {
    try {
      return print(1);
    } catch (Exception e) {
      return print(2);
    } finally {
      return  print(3);
    }
  }
}