Метод 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.
так что на самом деле это сводится к тому, что если метод имеет путь возврата, этот путь должен соответствовать типу возврата. Но есть примеры в байт-код, в котором вообще не генерируется обратный путь и, следовательно, не нарушается правило.