Pytanie 1:
Dlaczego poniższy kod kompiluje się bez instrukcji return?
public int a()
{
while(true);
}
Jest to objęte JLS§8.4.7 :
Jeśli metoda zostanie zadeklarowana jako typ zwracany (§ 8.4.5), wówczas błąd kompilacji wystąpi, jeśli treść metody może zakończyć się normalnie (§ 14.1).
Innymi słowy, metoda z typem zwracanym musi zwracać tylko za pomocą instrukcji return, która zapewnia zwrot wartości; metoda nie może „spaść z końca ciała”. Dokładne zasady dotyczące instrukcji return w treści metody znajdują się w § 14.17.
Metoda może mieć typ zwracany, ale nie zawiera instrukcji zwrotnych. Oto jeden przykład:
class DizzyDean {
int pitch() { throw new RuntimeException("90 mph?!"); }
}
Ponieważ kompilator wie, że pętla nigdy się nie kończy ( true
oczywiście zawsze jest to prawda), wie, że funkcja nie może „powrócić normalnie” (zrzucić koniec swojego ciała), a zatem jest w porządku, że jej nie ma return
.
Pytanie 2:
Z drugiej strony, dlaczego kompiluje następujący kod,
public int a()
{
while(0 == 0);
}
nawet jeśli poniższe nie.
public int a(int b)
{
while(b == b);
}
W takim 0 == 0
przypadku kompilator wie, że pętla nigdy się nie zakończy ( 0 == 0
zawsze będzie to prawdą). Ale nie wie o tym b == b
.
Dlaczego nie?
Kompilator rozumie wyrażenia stałe (§15.28) . Cytując § 15.2 - Formy wyrażeń (ponieważ dziwnie tego zdania nie ma w §15.28) :
Niektóre wyrażenia mają wartość, którą można określić podczas kompilacji. Są to wyrażenia stałe (§ 15,28).
W twoim b == b
przykładzie, ponieważ w grę wchodzi zmienna, nie jest to stałe wyrażenie i nie jest określone, aby miało być określone w czasie kompilacji. Widzimy , że zawsze będzie to prawdą w tym przypadku (chociaż gdyby b
było double
, jak wskazał QBrute , moglibyśmy się łatwo oszukać Double.NaN
, co nie==
jest samo w sobie ), ale JLS określa tylko, że wyrażenia stałe są określane w czasie kompilacji , nie pozwala kompilatorowi próbować oceniać wyrażeń niestałych. bayou.io podniósł dobrą rację, dlaczego nie: Jeśli zaczniesz iść drogą próbowania określenia wyrażeń obejmujących zmienne w czasie kompilacji, gdzie się zatrzymujesz? b == b
jest oczywiste (er, dlaNaN
wartości), ale co z tym a + b == b + a
? Czy (a + b) * 2 == a * 2 + b * 2
? Rysowanie linii na stałych ma sens.
Ponieważ więc nie „określa” wyrażenia, kompilator nie wie, że pętla nigdy się nie zakończy, więc uważa, że metoda może powrócić normalnie - czego nie wolno, ponieważ jest wymagana return
. Więc narzeka na brak return
.