Metoda Java z typem return kompiluje bez instrukcji return
Pytanie 1:
Dlaczego poniższy kod kompiluje się bez instrukcji return?
public int a() {
while(true);
}
Notice: If I add return after the while then I get an Unreachable Code Error
.
Pytanie 2:
Z drugiej strony, dlaczego poniższy kod kompiluje się,
public int a() {
while(0 == 0);
}
Mimo, że poniższe nie.
public int a(int b) {
while(b == b);
}
3 answers
Pytanie 1:
Dlaczego poniższy kod kompiluje się bez instrukcji return?
public int a() { while(true); }
To jest objęte JLS§8.4.7:
Jeśli metoda jest zadeklarowana jako typ zwracany (§8.4.5), to błąd w czasie kompilacji występuje, jeśli ciało metody może normalnie działać (§14.1).
Innymi słowy, metoda z typem return musi zwracać tylko za pomocą instrukcji return, która dostarcza wartość return; metoda nie może "upuścić końca ciała". Szczegółowe zasady dotyczące instrukcji return w ciele metody znajdują się w §14.17.
Możliwe jest, że metoda ma typ return i nie zawiera żadnych instrukcji return. Oto jeden przykład:
class DizzyDean { int pitch() { throw new RuntimeException("90 mph?!"); } }
Ponieważ kompilator wie, że pętla nigdy się nie zakończy (true
jest oczywiście zawsze prawdziwa), wie, że funkcja nie może "powrócić normalnie" (zrzucić koniec jej ciała), a zatem jest w porządku, że nie ma return
.
Pytanie 2:
Z drugiej strony, dlaczego poniższy kod się kompiluje,
public int a() { while(0 == 0); }
Mimo, że poniższe nie.
public int a(int b) { while(b == b); }
W przypadku 0 == 0
kompilator wie, że pętla nigdy się nie zakończy (że 0 == 0
zawsze będzie prawdą). Ale to Nie wie, że dla b == b
.
Kompilator rozumie wyrażenia stałe (§15.28). Cytowanie §15.2-formy wyrażeń (bo o dziwo to zdanie nie jest 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 przykładzie b == b
, ponieważ istnieje zmienna, nie jest ona wyrażeniem stałym i nie jest określona do określenia w czasie kompilacji. My widzimy, że w tym przypadku zawsze będzie to prawdą (chociaż jeśli b
były double
, Jak zauważył QBrute , możemy łatwo dać się zwieść Double.NaN
, czyli , a nie ==
samym ), ale JLS określa tylko, że stałe wyrażenia są ustalane w czasie kompilacji, nie pozwala kompilatorowi na próbę oceny niestałych wyrażeń. bayou.io poruszyłem słuszną kwestię dlaczego nie: jeśli zaczniesz próbować określić wyrażenia zawierające zmienne w czasie kompilacji, gdzie się zatrzymasz? b == b
jest oczywiste (er, dla wartości nie NaN
), ale co z a + b == b + a
? Albo (a + b) * 2 == a * 2 + b * 2
? Rysowanie linii w stałych ma sens.
Ponieważ 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 - co nie jest dozwolone, ponieważ wymagane jest użycie return
. Skarży się więc na brak return
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:34:43
Interesujące może być myślenie o metodzie zwracającej Typ nie jako obietnicy zwracania wartości określonego typu, ale jako obietnicy nie zwracania wartości, która jest nie określonego typu. W związku z tym, jeśli nigdy nic nie zwracasz, nie łamiesz obietnicy, a więc każda z poniższych rzeczy jest legalna: {]}
-
Looping forever:
X foo() { for (;;); }
-
Recursing forever:
X foo() { return foo(); }
-
Wyrzucanie wyjątek:
X foo() { throw new Error(); }
(uważam, że rekurencja jest zabawna: kompilator wierzy, że metoda zwróci wartość typu X
(cokolwiek to jest), ale nie jest to prawda, ponieważ nie ma kodu, który miałby pomysł, jak utworzyć lub pozyskać X
.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-30 03:21:08
Patrząc na kod bajtowy, jeśli to, co jest zwracane, nie pasuje do definicji, otrzymasz błąd kompilacji.
Przykład:
for(;;)
wyświetli bajtodes:
L0
LINENUMBER 6 L0
FRAME SAME
GOTO L0
Zwróć uwagę na brak jakiegokolwiek return bytecode
To nigdy nie uderza w Zwrot, a tym samym nie zwraca niewłaściwego typu.
Dla porównania metoda taka jak:
public String getBar() {
return bar;
}
Zwróci następujące bajtowe kody:
public java.lang.String getBar();
Code:
0: aload_0
1: getfield #2; //Field bar:Ljava/lang/String;
4: areturn
Zwróć uwagę na "areturn", który oznacza "return a reference"
Teraz jeśli zrobimy co następuje:
public String getBar() {
return 1;
}
Zwróci następujące bajtowe kody:
public String getBar();
Code:
0: iconst_1
1: ireturn
Teraz widzimy, że typ w definicji nie pasuje do zwracanego typu ireturn, co oznacza return int.
Tak naprawdę sprowadza się to do tego, że jeśli metoda ma ścieżkę zwrotną, to ta ścieżka musi być zgodna z typem zwrotnym. Są jednak instancje w kodzie bajtowym, w których nie jest generowana ścieżka powrotna, a tym samym nie ma łamania reguły.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-30 22:02:15