Perché Java ha un errore del compilatore “istruzione non raggiungibile”?

Spesso trovo che quando si esegue il debug di un programma sia conveniente (anche se discutibilmente discutibile) inserire un’istruzione return in un blocco di codice. Potrei provare qualcosa di simile in Java ….

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"); } } 

naturalmente, questo produrrebbe l’errore del compilatore.

Test.java:7: istruzione irraggiungibile

Potrei capire perché un avvertimento potrebbe essere giustificato in quanto il codice inutilizzato è una ctriggers pratica. Ma non capisco perché questo debba generare un errore.

È solo Java che cerca di essere una tata, o c’è una buona ragione per fare di questo un errore del compilatore?

    Perché il codice irraggiungibile non ha significato per il compilatore. Mentre rendere il codice significativo per le persone è sia di primaria importanza sia più difficile che renderlo significativo per un compilatore, il compilatore è il consumatore essenziale del codice. I progettisti di Java prendono il punto di vista che il codice che non è significativo per il compilatore è un errore. La loro posizione è che se hai un codice irraggiungibile, hai commesso un errore che deve essere corretto.

    C’è una domanda simile qui: codice irraggiungibile: errore o avviso? , in cui l’autore dice “Personalmente sento fortemente che dovrebbe essere un errore: se il programmatore scrive un pezzo di codice, dovrebbe sempre essere con l’intenzione di eseguirlo effettivamente in qualche scenario”. Ovviamente i designer di linguaggio di Java sono d’accordo.

    Se il codice irraggiungibile debba impedire la compilazione è una domanda sulla quale non ci sarà mai consenso. Ma questo è il motivo per cui i designer Java l’hanno fatto.


    Un numero di persone nei commenti sottolinea che ci sono molte classi di codice irraggiungibile che Java non impedisce la compilazione. Se capisco le conseguenze di Gödel correttamente, nessun compilatore può catturare tutte le classi di codice irraggiungibile.

    I test unitari non possono catturare ogni singolo bug. Non usiamo questo come argomento contro il loro valore. Allo stesso modo un compilatore non può catturare tutto il codice problematico, ma è comunque utile per impedire la compilazione di codice errato quando ansible.

    I progettisti del linguaggio Java considerano un codice irraggiungibile un errore. Quindi evitare di compilare quando ansible è ragionevole.


    (Prima di downvotare: la domanda non è se Java debba avere un errore del compilatore di istruzione non raggiungibile.La domanda è perché Java ha un errore del compilatore di istruzioni non raggiungibile. Non mi sottovalutare solo perché pensi che Java abbia preso una decisione di progettazione sbagliata.)

    Non c’è una ragione definitiva per cui le affermazioni irraggiungibili non devono essere consentite; altre lingue consentono loro senza problemi. Per le tue esigenze specifiche, questo è il solito trucco:

     if (true) return; 

    Sembra assurdo, chiunque legge il codice indovina che deve essere stato fatto deliberatamente, non un errore incurante di lasciare il resto delle affermazioni irraggiungibili.

    Java ha un piccolo supporto per “compilazione condizionale”

    http://java.sun.com/docs/books/jls/third_edition/html/statements.html#14.21

     if (false) { x=3; } 

    non risulta in un errore in fase di compilazione. Un compilatore ottimizzante può rendersi conto che l’istruzione x = 3; non verrà mai eseguito e potrebbe scegliere di omettere il codice per tale istruzione dal file di class generato, ma l’istruzione x = 3; non è considerato “irraggiungibile” nel senso tecnico qui specificato.

    La logica di questo diverso trattamento è di consentire ai programmatori di definire “variabili di flag” come:

     static final boolean DEBUG = false; 

    e quindi scrivere il codice come:

     if (DEBUG) { x=3; } 

    L’idea è che dovrebbe essere ansible modificare il valore di DEBUG da falso a vero o da vero a falso e quindi compilare il codice correttamente senza altre modifiche al testo del programma.

    È la tata. Sento che .Net ha ottenuto questo giusto – solleva un avvertimento per codice irraggiungibile, ma non un errore. È bene essere avvertiti a riguardo, ma non vedo alcun motivo per impedire la compilazione (specialmente durante le sessioni di debugging in cui è opportuno inserire un ritorno per aggirare il codice).

    Ho appena notato questa domanda e ho voluto aggiungere $ 0,02 a questo.

    In caso di Java, questa non è in realtà un’opzione. L’errore “codice irraggiungibile” non deriva dal fatto che gli sviluppatori JVM pensavano di proteggere gli sviluppatori da qualsiasi cosa, o di essere più vigili, ma dai requisiti della specifica JVM.

    Sia il compilatore Java che JVM utilizzano ciò che viene chiamato “stack maps”, ovvero un’informazione definita su tutti gli elementi nello stack, come allocati per il metodo corrente. Il tipo di ogni slot della pila deve essere noto, in modo che un’istruzione JVM non maltratta l’elemento di un tipo per un altro tipo. Questo è per lo più importante per evitare di avere un valore numerico mai usato come puntatore. È ansible, utilizzando l’assembly Java, provare a inserire / memorizzare un numero, ma poi eseguire il pop / caricamento di un riferimento a un object. Tuttavia, JVM rifiuterà questo codice durante la convalida della class, ovvero quando le mappe dello stack vengono create e testate per coerenza.

    Per verificare le mappe dello stack, la VM deve percorrere tutti i percorsi di codice esistenti in un metodo e assicurarsi che indipendentemente da quale percorso di codice verrà mai eseguito, i dati dello stack per ogni istruzione concordano con ciò che qualsiasi codice precedente ha spinto / memorizzato nello stack. Quindi, nel caso semplice di:

     Object a; if (something) { a = new Object(); } else { a = new String(); } System.out.println(a); 

    alla riga 3, JVM controllerà che entrambi i rami di “if” siano stati memorizzati solo in un object (che è solo la var # 0 locale) qualcosa che sia compatibile con Object (dal momento che il codice dalla riga 3 e su tratterà la var # 0 locale ).

    Quando il compilatore arriva a un codice irraggiungibile, non sa in che stato lo stack potrebbe essere a quel punto, quindi non può verificarne lo stato. A quel punto non è più ansible compilare il codice, poiché non può tenere traccia delle variabili locali, quindi, invece di lasciare questa ambiguità nel file di class, produce un errore fatale.

    Ovviamente una semplice condizione come if (1<2) la ingannasse, ma non è davvero una sciocchezza - gli sta dando un potenziale ramo che può portare al codice, e almeno sia il compilatore che la VM possono determinare, come lo stack gli oggetti possono essere utilizzati da lì in poi.

    PS Non so cosa faccia .NET in questo caso, ma credo che fallirà anche la compilazione. Questo normalmente non sarà un problema per qualsiasi compilatore di codice macchina (C, C ++, Obj-C, ecc.)

    Uno degli obiettivi dei compilatori è escludere classi di errori. Alcuni codici irraggiungibili sono lì per caso, è bello che javac escluda quella class di errore in fase di compilazione.

    Per ogni regola che cattura codice errato, qualcuno vorrà che il compilatore lo accetti perché sa cosa sta facendo. Questa è la penalità del controllo del compilatore, e ottenere il giusto equilibrio è uno dei punti più difficili del design del linguaggio. Anche con il controllo più rigoroso c’è ancora un numero infinito di programmi che possono essere scritti, quindi le cose non possono essere così brutte.

    Mentre penso che questo errore del compilatore sia una buona cosa, c’è un modo per aggirare il problema. Usa una condizione che sai sarà vera:

     public void myMethod(){ someCodeHere(); if(1 < 2) return; // compiler isn't smart enough to complain about this moreCodeHere(); } 

    Il compilatore non è abbastanza intelligente da lamentarsi di ciò.

    È certamente una buona cosa lamentarsi del fatto che il compilatore è il migliore, per quanto possa permettervi di fare ciò di cui avete bisogno. Di solito il piccolo prezzo da pagare è quello di commentare il codice, il guadagno è che quando si compila il codice funziona. Un esempio generale è Haskell su cui le persone urlano fino a quando non si rendono conto che il loro test / debug è solo test principale e breve. Personalmente, in Java, non eseguo quasi il debugging pur essendo (di fatto apposta) non attento.

    Se il motivo per consentire if (aBooleanVariable) return; someMoreCode; if (aBooleanVariable) return; someMoreCode; è consentire le bandiere, quindi il fatto che if (true) return; someMoreCode; if (true) return; someMoreCode; non genera un errore in fase di compilazione sembra incoerenza nella politica di generare un’eccezione CodeNotReachable, poiché il compilatore ‘sa’ che true non è un flag (non una variabile).

    Altri due modi che potrebbero essere interessanti, ma non si applicano alla distriggerszione di parte del codice di un metodo e anche if (true) return :

    Ora, invece di dire if (true) return; si potrebbe voler dire assert false e aggiungere -ea OR -ea package OR -ea className agli argomenti jvm. Il punto buono è che ciò consente una certa granularità e richiede l’aggiunta di un parametro aggiuntivo al richiamo di jvm, quindi non c’è bisogno di impostare un flag DEBUG nel codice, ma con un argomento aggiunto in runtime, che è utile quando il target non è il la macchina dello sviluppatore e la ricompilazione e il trasferimento di bytecode richiedono tempo.

    Esiste anche il modo System.exit(0) , ma questo potrebbe essere eccessivo, se lo si inserisce in Java in un JSP, il server terminerà.

    A prescindere dal fatto che Java è un linguaggio di design “da bambinaia”, preferirei usare qualcosa di nativo come C / C ++ per un maggiore controllo.