Вопрос 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);
}
java
syntax
while-loop
compilation
return
Вилли Ментцель
источник
источник
Ответы:
Это покрыто JLS§8.4.7 :
Поскольку компилятор знает, что цикл никогда не завершится (
true
конечно, всегда верно), он знает, что функция не может «нормально возвращаться» (опускать конец своего тела), и, таким образом, все в порядке, что нетreturn
.В этом
0 == 0
случае компилятор знает, что цикл никогда не прекратится (это0 == 0
всегда будет истиной). Но это не знает, что дляb == b
.Почему нет?
Компилятор понимает константные выражения (§15.28) . Цитирование §15.2 - Формы выражений (потому что, как ни странно, этого предложения нет в §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
(независимо от того, что это), но это не так, потому что нет кода, который имел бы представление о том, как создать или обеспечитьX
.)источник
Глядя на байт-код, если возвращаемое не соответствует определению, вы получите ошибку компиляции.
Пример:
for(;;)
покажет байт-коды:Обратите внимание на отсутствие какого-либо возврата байт-кода
Это никогда не приводит к возврату и, следовательно, не возвращает неправильный тип.
Для сравнения, такой метод:
Вернет следующие байт-коды:
Обратите внимание на «areturn», что означает «вернуть ссылку»
Теперь, если мы сделаем следующее:
Вернет следующие байт-коды:
Теперь мы можем видеть, что тип в определении не соответствует типу возврата ireturn, что означает возврат int.
Поэтому на самом деле все сводится к тому, что если метод имеет путь возврата, этот путь должен соответствовать типу возврата. Но в байт-коде есть случаи, когда путь возврата вообще не генерируется, и, следовательно, не нарушается правило.
источник