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 ( trueoczywiś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 == 0przypadku kompilator wie, że pętla nigdy się nie zakończy ( 0 == 0zawsze 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 == bprzykł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 bbył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 == bjest oczywiste (er, dlaNaNwartoś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.