Często stwierdzam, że podczas debugowania programu wygodne jest (choć może to być złą praktyką) wstawienie instrukcji return wewnątrz bloku kodu. Mogę spróbować czegoś takiego w Javie ...
class Test {
public static void main(String args[]) {
System.out.println("hello world");
return;
System.out.println("i think this line might cause a problem");
}
}
oczywiście spowodowałoby to błąd kompilatora.
Test.java:7: nieosiągalne oświadczenie
Mogłem zrozumieć, dlaczego ostrzeżenie może być uzasadnione, ponieważ posiadanie nieużywanego kodu jest złą praktyką. Ale nie rozumiem, dlaczego ma to powodować błąd.
Czy to po prostu Java próbuje być nianią, czy jest dobry powód, aby uczynić to błędem kompilatora?
java
language-design
unreachable-statement
Mikrofon
źródło
źródło
Odpowiedzi:
Ponieważ nieosiągalny kod jest bez znaczenia dla kompilatora. Chociaż uczynienie kodu zrozumiałym dla ludzi jest zarówno najważniejsze, jak i trudniejsze niż uczynienie go zrozumiałym dla kompilatora, kompilator jest podstawowym konsumentem kodu. Projektanci Javy uważają, że kod, który nie jest zrozumiały dla kompilatora, jest błędem. Ich stanowisko jest takie, że jeśli masz jakiś nieosiągalny kod, popełniłeś błąd, który należy naprawić.
Tutaj pojawia się podobne pytanie: Nieosiągalny kod: błąd czy ostrzeżenie? , w którym autor mówi: „Osobiście uważam, że to powinien być błąd: jeśli programista pisze fragment kodu, zawsze powinien mieć zamiar uruchomić go w jakimś scenariuszu”. Oczywiście projektanci języka Java zgadzają się z tym.
Czy nieosiągalny kod powinien uniemożliwić kompilację, jest kwestią, w której nigdy nie będzie konsensusu. Ale właśnie dlatego zrobili to projektanci Java.
Wiele osób w komentarzach zwraca uwagę, że istnieje wiele klas niedostępnego kodu. Java nie zapobiega kompilacji. Jeśli dobrze rozumiem konsekwencje Gödla, żaden kompilator nie jest w stanie złapać wszystkich klas nieosiągalnego kodu.
Testy jednostkowe nie mogą wychwycić każdego pojedynczego błędu. Nie używamy tego jako argumentu przeciwko ich wartości. Podobnie kompilator nie może wychwycić całego problematycznego kodu, ale nadal jest cenny, aby zapobiec kompilacji złego kodu, gdy jest to możliwe.
Projektanci języka Java uważają nieosiągalny kod za błąd. Dlatego zapobieganie kompilacji, gdy jest to możliwe, jest rozsądne.
(Zanim zagłosujesz: nie chodzi o to, czy Java powinna mieć błąd kompilatora instrukcji nieosiągalnych, czy nie. Pytanie brzmi, dlaczego Java ma błąd kompilatora instrukcji nieosiągalnych. Nie oceniaj mnie negatywnie tylko dlatego, że uważasz, że Java podjęła złą decyzję projektową).
źródło
return; System.out.println();
iif(true){ return; } System.out.println();
mają takie samo zachowanie, ale jeden jest błędem kompilacji, a drugi nie. Tak więc, kiedy debuguję i używam tej metody do tymczasowego „komentowania” kodu, tak to robię. Problem z wykomentowaniem kodu polega na tym, że musisz znaleźć odpowiednie miejsce, aby zatrzymać komentarz, co może zająć więcej czasu niż wrzuceniereturn;
naprawdę szybkiego.Nie ma ostatecznego powodu, dla którego nie można dopuścić nieosiągalnych instrukcji; inne języki pozwalają na to bez problemów. Oto zwykła sztuczka dla twojej konkretnej potrzeby:
if (true) return;
Wygląda to bezsensownie, każdy, kto czyta kod, domyśli się, że musiało to być zrobione celowo, a nie nieostrożny błąd polegający na pozostawieniu reszty wypowiedzi nieosiągalnych.
Java ma niewielkie wsparcie dla „kompilacji warunkowej”
http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21
źródło
if (true) return;
nie wskazuje, DLACZEGO pomijasz logikę, podczas gdyif (BEHAVIOURDISABLED) return;
komunikujesz zamiar.To jest niania. Wydaje mi się, że .Net ma rację - generuje ostrzeżenie o nieosiągalnym kodzie, ale nie jest to błąd. Dobrze jest być przed tym ostrzeżonym, ale nie widzę powodu, aby zapobiegać kompilacji (szczególnie podczas sesji debugowania, gdzie dobrze jest wrzucić powrót, aby ominąć jakiś kod).
źródło
Dopiero co zauważyłem to pytanie i chciałem dodać do niego 0,02 $.
W przypadku Javy nie ma takiej możliwości. Błąd „nieosiągalnego kodu” nie wynika z faktu, że programiści JVM myśleli, że powinni chronić programistów przed czymkolwiek lub zachować szczególną czujność, ale z wymagań specyfikacji JVM.
Zarówno kompilator Java, jak i JVM używają tak zwanych „map stosu” - określonej informacji o wszystkich elementach stosu, przydzielonych dla bieżącej metody. Typ każdej szczeliny stosu musi być znany, aby instrukcja JVM nie traktowała elementu jednego typu dla innego typu. Jest to szczególnie ważne, aby zapobiec używaniu wartości liczbowej jako wskaźnika. Jest możliwe, używając zestawu Java, aby spróbować wypchnąć / zapisać liczbę, a następnie pobrać / załadować odwołanie do obiektu. Jednak JVM odrzuci ten kod podczas sprawdzania poprawności klas, czyli wtedy, gdy mapy stosu są tworzone i testowane pod kątem spójności.
Aby zweryfikować mapy stosu, maszyna wirtualna musi przejść przez wszystkie ścieżki kodu istniejące w metodzie i upewnić się, że bez względu na to, która ścieżka kodu zostanie kiedykolwiek wykonana, dane stosu dla każdej instrukcji są zgodne z tym, co przekazał poprzedni kod / przechowywane w stosie. A więc w prostym przypadku:
Object a; if (something) { a = new Object(); } else { a = new String(); } System.out.println(a);
w linii 3, JVM sprawdzi, czy obie gałęzie 'if' zapisały tylko w (która jest po prostu lokalną zmienną # 0) czymś, co jest kompatybilne z Object (ponieważ tak kod z linii 3 i następnych będzie traktował lokalną zmienną # 0 ).
Kiedy kompilator dociera do nieosiągalnego kodu, nie do końca wie, w jakim stanie może być stos w tym momencie, więc nie może zweryfikować jego stanu. W tym momencie nie może już skompilować kodu, ponieważ nie może również śledzić zmiennych lokalnych, więc zamiast pozostawić tę niejednoznaczność w pliku klasy, generuje błąd krytyczny.
Oczywiście prosty warunek, taki jak
if (1<2)
, oszuka go, ale tak naprawdę nie jest to głupi - daje mu potencjalną gałąź, która może prowadzić do kodu, a przynajmniej zarówno kompilator, jak i maszyna wirtualna mogą określić, w jaki sposób elementy stosu mogą być stamtąd używane na.PS Nie wiem, co robi .NET w tym przypadku, ale sądzę, że kompilacja również się nie powiedzie. Zwykle nie będzie to problemem dla żadnych kompilatorów kodu maszynowego (C, C ++, Obj-C itp.)
źródło
{ [flowingstatement;]* }
=>flowingstatement
,{ [flowingstatement;]* stoppingstatement; }
=>stoppingstatement
,return
=>stoppingstatement
,if (condition) stoppingstatement; else stoppingstatement
=>stoppingstatement;
Itd.?void blah() { try {return;} catch (RuntimeError ex) { } DoSomethingElse(); }
Czy Java wydałaby dźwięk, że nie ma możliwości wyjścia ztry
bloku przez wyjątek, czy też wyliczyłby, że w dowolnymtry
bloku mógłby wystąpić jakikolwiek niezaznaczony wyjątek ?if (constantThatEqualsFive < 3) {doSomething;};
.Jednym z celów kompilatorów jest wykluczenie klas błędów. Jakiś nieosiągalny kod jest tam przez przypadek, fajnie, że javac wyklucza tę klasę błędów w czasie kompilacji.
Dla każdej reguły, która wyłapuje błędny kod, ktoś będzie chciał, aby kompilator ją zaakceptował, ponieważ wie, co robi. Taka jest kara za sprawdzanie kompilatora, a uzyskanie właściwej równowagi jest jednym z najtrudniejszych punktów projektowania języka. Nawet przy najsurowszych kontrolach wciąż istnieje nieskończona liczba programów, które można napisać, więc nie może być tak źle.
źródło
Chociaż myślę, że ten błąd kompilatora to dobra rzecz, jest sposób na obejście tego problemu. Użyj warunku, o którym wiesz, że będzie prawdziwy:
public void myMethod(){ someCodeHere(); if(1 < 2) return; // compiler isn't smart enough to complain about this moreCodeHere(); }
Kompilator nie jest wystarczająco inteligentny, aby narzekać na to.
źródło
1<2
to prawda, a reszta metody nie musi być uwzględniona w kodzie bajtowym. zasady „nieosiągalnych stwierdzeń” muszą być jasne, ustalone i niezależne od sprytu kompilatorów.Z pewnością dobrze jest narzekać, im bardziej rygorystyczny kompilator jest lepszy, o ile pozwala na zrobienie tego, czego potrzebujesz. Zwykle niewielką ceną jest skomentowanie kodu, a korzyścią jest to, że kompilacja kodu działa. Ogólnym przykładem jest Haskell, o którym ludzie krzyczą, dopóki nie zdadzą sobie sprawy, że ich test / debugowanie jest tylko głównym i krótkim testem. Osobiście w Javie prawie nie debuguję, będąc (w rzeczywistości celowo) nieuważnym.
źródło
Jeśli powodem zezwolenia
if (aBooleanVariable) return; someMoreCode;
jest zezwolenie na flagi, to fakt, żeif (true) return; someMoreCode;
nie generuje błędu czasu kompilacji, wydaje się niespójnością w polityce generowania wyjątku CodeNotReachable, ponieważ kompilator „wie”, żetrue
nie jest to flaga (a nie zmienna).Dwa inne sposoby, które mogą być interesujące, ale nie mają zastosowania do wyłączania części kodu metody, a także
if (true) return
:Zamiast mówić,
if (true) return;
możesz powiedziećassert false
i dodać-ea OR -ea package OR -ea className
do argumentów jvm. Zaletą jest to, że pozwala to na pewną szczegółowość i wymaga dodania dodatkowego parametru do wywołania jvm, więc nie ma potrzeby ustawiania flagi DEBUG w kodzie, ale przez dodanie argumentu w czasie wykonywania, co jest przydatne, gdy cel nie jest programista, a rekompilacja i transfer kodu bajtowego zajmuje trochę czasu.Jest też
System.exit(0)
sposób, ale może to być przesada, jeśli umieścisz go w Javie na stronie JSP, spowoduje to zamknięcie serwera.Poza tym Java jest z założenia językiem „niania”, wolałbym raczej użyć czegoś natywnego, takiego jak C / C ++, aby uzyskać większą kontrolę.
źródło
static final
zmiennej, która jest ustawiona w statycznym bloku inicjalizacji, nastatic final
zmienną, która jest ustawiona w jej deklaracji, nie powinna być istotną zmianą. Jest całkowicie prawdopodobne, że kiedy klasa jest zapisywana po raz pierwszy, może czasami nie być w stanie czegoś zrobić (i nie wiadomo do czasu wykonania, czy będzie w stanie to zrobić, czy nie), ale późniejsze wersje klasy mogły być w stanie rób to zawsze. ZmianamyClass.canFoo
na stałą powinna byćif (myClass.canFoo) myClass.Foo(); else doSomethingElse();
bardziej wydajna - a nie przerywać.