Метод 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 ответа:
Вопрос 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
.
может быть интересно думать о типе возврата метода не как обещание вернуть значение указанного типа, а как обещание не чтобы вернуть значение не указанного типа. Таким образом, если вы никогда ничего не возвращаете, вы не нарушаете обещание, и поэтому любое из следующих действий является законным:
зацикливание навсегда:
X foo() { for (;;); }
рекурсией навсегда:
X foo() { return foo(); }
выбрасывает исключение:
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.
так что на самом деле это сводится к тому, что если метод имеет путь возврата, этот путь должен соответствовать типу возврата. Но есть примеры в байт-код, в котором вообще не генерируется обратный путь и, следовательно, не нарушается правило.