Метод Java с возвращаемым типом компилируется без оператора return


Вопрос 1:

Почему следующий код компилируется без оператора return?

public int a() {
    while(true);
}

обратите внимание: если я добавлю возврат через некоторое время, то я получаю Unreachable Code Error.

Вопрос 2:

С другой стороны, почему после компиляции кода

public int a() {
    while(0 == 0);
}

даже если следующее не делает.

public int a(int b) {
    while(b == b);
}
3 222

3 ответа:

Вопрос 1:

почему следующий код компилируется без оператора return?

public int a() 
{
    while(true);
}

этот JLS§8.4.7:

если метод объявлен с типом возвращаемого значения (§8.4.5), то ошибка времени компиляции возникает, если тело метода может завершиться нормально (§14.1).

другими словами, метод с типом возврата должен возвращать только используя оператор return, который обеспечивает возврат значения; метод не может "отбросить конец своего тела". См. §14.17 для точных правил о операторах return в теле метода.

метод может иметь тип возвращаемого значения и при этом не содержать операторов return. Вот один пример:

class DizzyDean {
    int pitch() { throw new RuntimeException("90 mph?!"); }
}

так как компилятор знает, что цикл никогда не завершится (true всегда верно, конечно), он знает, что функция не может " вернуть обычно" (опустите конец своего тела), и поэтому все в порядке, что нет return.

Вопрос 2:

С другой стороны, почему после компиляции кода

public int a() 
{
    while(0 == 0);
}

даже если следующее не делает.

public int a(int b)
{
    while(b == b);
}

на 0 == 0 случай, компилятор знает, что цикл никогда не завершится (что 0 == 0 всегда будет true). Но это не знаю, что для b == b.

почему бы и нет?

компилятор понимает константных выражений (§15.28). Цитирование §15.2 - формы выражения(потому что странно это предложение не в §15.28):

некоторые выражения имеют значение, которое может быть определено во время компиляции. Это постоянные выражения (§15.28).

в своем b == b пример, потому что есть переменная он не является постоянным выражением и не указан для определения во время компиляции. мы видим, что это всегда будет верно в этом случае (хотя если b были double, как QBrute указал, нас легко может обмануть Double.NaN, которая составляет не ), но JLS только указывает, что константные выражения определяются во время компиляции, это не позволяет компилятору пытаться вычислить непостоянные выражения. bayou.io поднял хороший вопрос почему бы и нет: если вы начнете пытаться определить выражения, включающие переменные во время компиляции, где вы остановитесь? b == b очевидно (Э, для не -NaN значения), но как насчет a + b == b + a? Или (a + b) * 2 == a * 2 + b * 2? Рисование линии на константах имеет смысл.

так как он не "определяет" выражение, компилятор не знает, что цикл никогда не завершится, поэтому он думает, что метод может возврат обычно-что это не разрешено делать, потому что требуется использовать return. Так что он жалуется на отсутствие return.

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

  1. зацикливание навсегда:

    X foo() {
        for (;;);
    }
    
  2. рекурсией навсегда:

    X foo() {
        return foo();
    }
    
  3. выбрасывает исключение:

    X foo() {
        throw new Error();
    }
    

(Я нахожу рекурсию одной забавой думать: компилятор считает, что метод вернет значение типа X (что бы это ни было), но это не так, потому что нет кода, который имеет какое-либо представление о том, как создать или получить X.)

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

пример:

for(;;) покажет байт-коды:

L0
    LINENUMBER 6 L0
    FRAME SAME
    GOTO L0

обратите внимание на отсутствие какого-либо байт-кода возврата

это никогда не ударил возврат, и, таким образом, не возвращает неправильный тип.

для сравнения, такой метод, как:

public String getBar() { 
    return bar; 
}

возвращает следующее байт-коды:

public java.lang.String getBar();
    Code:
      0:   aload_0
      1:   getfield        #2; //Field bar:Ljava/lang/String;
      4:   areturn

обратите внимание на "areturn", что означает "вернуть ссылку"

теперь, если мы делаем следующее:

public String getBar() { 
    return 1; 
}

возвращает следующий байт-код:

public String getBar();
  Code:
   0:   iconst_1
   1:   ireturn

теперь мы видим, что тип в определении не соответствует возвращаемому типу ireturn, что означает return int.

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