2010-01-26 14 views
51

Qualcuno sa perché:Errore di codice irraggiungibile rispetto a dead code warning in Java in Eclipse?

public void foo() 
{ 
    System.out.println("Hello"); 
    return; 
    System.out.println("World!"); 
} 

sarebbe segnalato come un "errore non raggiungibile" in Eclipse, ma

public void foo() 
{ 
    System.out.println("Hello"); 
    if(true) return; 
    System.out.println("World!"); 
} 

innesca solo un avvertimento "Dead codice"?

L'unica spiegazione che posso pensare è che il compilatore Java contrassegna solo il primo e che un'ulteriore analisi in Eclipse calcola il secondo. Tuttavia, se questo è il caso, perché il compilatore Java non può capire questo caso al momento della compilazione?

Non sarebbe il compilatore Java a capire al momento della compilazione che if (true) non ha alcun effetto, ottenendo così bytecode che è essenzialmente identico? A che punto è applicata l'analisi del codice raggiungibile?

Immagino che un modo più generale di pensare a questa domanda sia: "quando viene applicata l'analisi del codice raggiungibile"? Nella trasformazione del secondo frammento di codice Java nel bytecode finale, sono sicuro che ad un certo punto l'equivalente di runtime "if (true)" viene rimosso e le rappresentazioni dei due programmi diventano identiche. Il compilatore Java non applicherà di nuovo l'analisi del codice raggiungibile?

risposta

33

Il primo è non compilare (hai ottenuto un errore), la seconda compila (hai appena ricevuto un avviso). Questa è la differenza.

Quanto al motivo per Eclipse rileva codice morto, beh, questo è solo la comodità di uno strumento di sviluppo integrato con un compilatore built-in che può essere finetuned più in contrasto con JDK per rilevare questo tipo di codice.

Aggiornamento: il JDK elimina effettivamente il codice guasto.

public class Test { 
    public void foo() { 
     System.out.println("foo"); 
     if(true)return; 
     System.out.println("foo"); 
    } 
    public void bar() { 
     System.out.println("bar"); 
     if(false)return; 
     System.out.println("bar"); 
    } 
} 

javap -c dice:

 
public class Test extends java.lang.Object{ 
public Test(); 
    Code: 
    0: aload_0 
    1: invokespecial #1; //Method java/lang/Object."":()V 
    4: return 

public void foo(); 
    Code: 
    0: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: ldc    #3; //String foo 
    5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/StrV 
    8: return 

public void bar(); 
    Code: 
    0: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    3: ldc    #5; //String bar 
    5: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    8: getstatic  #2; //Field java/lang/System.out:Ljava/io/PrintStream; 
    11: ldc    #5; //String bar 
    13: invokevirtual #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V 
    16: return 

} 

Per quanto riguarda il motivo per cui (Sun) non dà un avvertimento di questo, non ho idea :) Almeno il compilatore JDK ha in realtà DCE (Dead Code Eliminazione) incorporato.

+0

Ma perché non il compilatore Java eliminare il se (vero) al momento della compilazione e quindi identificare l'errore quando analizza il codice raggiungibile? – Uri

+2

Hey Uri, puoi sostituire "if (true)" con "boolean x = true; if (x)". Non è compito del compilatore eliminarlo, perché è possibile, ad esempio, inserire un punto di interruzione e modificare il valore di x durante l'esecuzione – Yoni

+0

Si spera che la mia risposta (modificata un po 'dalla prima volta che l'ho vista) lo renderà un po' più chiaro. –

2

Il if (true) è un po 'più sottile di "irraggiungibile"; poiché tale codifica codificata return renderà sempre il seguente codice non raggiungibile, ma la modifica della condizione in if potrebbe rendere raggiungibile la seguente istruzione.

Avere un condizionale significa che c'è una possibilità che potrebbe cambiare. Ci sono casi in cui qualcosa di più complicato di uno true è tra parentesi, e non è ovvio per il lettore umano che il seguente codice è "disattivato", ma il compilatore si accorge, quindi è in grado di avvisarti.

Eclipse è menzionato qui e rende le cose un po 'più complicate per l'utente; ma in realtà sotto Eclipse è solo un (molto sofisticato) compilatore Java che ha molti interruttori per gli avvisi ecc. che Eclipse può accendere e spegnere. In altre parole, non si ottiene abbastanza l'ampiezza di diversi avvisi/errori da una compilazione diretta javac, né si dispone di mezzi convenienti per attivarli o disattivarli. Ma è lo stesso affare, solo con più campane e fischietti.

+0

Non sono sicuro di capire la sottigliezza qui. Come può cambiare la condizione? Dal momento che utilizzo il letterale, non c'è modo di cambiarlo in fase di esecuzione. – Uri

+0

L'esempio di Yoni sotto la risposta di BalusC è un bell'esempio pratico di ciò che stavo cercando di dire nel mio secondo paragrafo. –

0

La differenza è nella semantica tra tempo di esecuzione e tempo di compilazione. Nel tuo secondo esempio, il codice viene compilato in un ramo if-else nel bytecode ed eclipse è semplicemente abbastanza intelligente da dirti che la parte else non verrà mai raggiunta in runtime. Eclipse ti avvisa solo perché è ancora un codice legale.

Nel primo esempio, si tratta di un errore perché il codice è illegale dalla definizione di java. Il compilatore non ti consente di creare codice byte con istruzioni non raggiungibili.

25

Il codice non raggiungibile è un errore in base allo Java Language Spec.

Per citare dal JLS:

The idea is that there must be some possible execution path from the beginning of the constructor, method, instance initializer or static initializer that contains the statement to the statement itself. The analysis takes into account the structure of statements. Except for the special treatment of while, do, and for statements whose condition expression has the constant value true, the values of expressions are not taken into account in the flow analysis.

Ciò significa, è che il blocco if non viene presa in considerazione, dal momento che se si passa attraverso uno dei percorsi della dichiarazione if, si potrebbe raggiungere la finale dichiarazione di stampa. Se è stato modificato il codice per essere:

public void foo() { 
    System.out.println("Hello"); 
    if (true) 
     return; 
    else 
     return; 
    System.out.println("World!"); 
} 

poi improvvisamente non sarebbe più a compilare, dal momento che non v'è alcun percorso attraverso l'affermazione if che permetterebbe l'ultima riga da raggiungere.

Cioè, un compilatore compatibile con Java non è autorizzato a compilare il primo frammento di codice. Per citare ulteriormente il JLS:

As an example, the following statement results in a compile-time error:

while (false) { x=3; } 

because the statement x=3; is not reachable; but the superficially similar case:

if (false) { x=3; } 

does not result in a compile-time error. An optimizing compiler may realize that the statement x=3; will never be executed and may choose to omit the code for that statement from the generated class file, but the statement x=3; is not regarded as "unreachable" in the technical sense specified here.

Il secondo avvertimento che Eclipse dà, sul codice morto, è un avvertimento generato dal compilatore, che non è "irraggiungibile" , secondo il JLS, ma in pratica lo è. Questo è un ulteriore controllo di stile lint fornito da Eclipse. Questo è del tutto opzionale e, usando la configurazione di Eclipse, può essere disabilitato, o trasformato in un errore del compilatore invece di un avvertimento.

Questo secondo blocco è un "codice odore", i blocchi if (false) vengono normalmente inseriti per disabilitare il codice per scopi di debug, averlo lasciato in genere è accidentale, e quindi l'avviso.

Infatti, Eclipse esegue test ancora più avanzati per determinare i possibili valori per un'istruzione if per determinare se sia possibile prendere entrambi i percorsi. Ad esempio, Eclipse potrebbe anche lamentarsi codice morto nel metodo seguente:

public void foo() { 
    System.out.println("Hello"); 
    boolean bool = Random.nextBoolean(); 
    if (bool) 
     return; 
    if (bool || Random.nextBoolean()) 
     System.out.println("World!"); 
} 

verrà generato un codice irraggiungibile per la seconda istruzione if, dal momento che può ragionare che bool deve essere solo false a questo punto del codice . In un frammento di codice così breve è ovvio che le due istruzioni if ​​stanno testando la stessa cosa, tuttavia se ci sono 10-15 linee di codice nel mezzo potrebbe non essere più così ovvio.

Quindi, in sintesi, la differenza tra i due: uno è vietato dal JLS, e uno non lo è, ma viene rilevato da Eclipse come servizio per il programmatore.

+0

È interessante notare che non includevano "se" nell'elenco delle dichiarazioni di flusso di controllo con "trattamento speciale". Mi chiedo perché? – meriton

+2

Oh, ho capito: il trattamento speciale è quello di rilevare ovvi cicli infiniti. 'If' non è un'istruzione ciclica. (Sono un po 'lento oggi ...) – meriton

+0

@meriton: Ho aggiunto la sezione appropriata dalla JLS per mostrare cosa stavano cercando di risolvere. –

2

Penso che un modo per farlo è che il codice non raggiungibile è molto probabilmente un errore e il JLS cerca di proteggerti da tali errori.

Permettere il if (true) return; è un buon modo per aggirare la limitazione di JLS se si vuole effettivamente farlo apposta. Se il JLS lo fermasse, sarebbe d'intralcio. Inoltre, dovrebbe anche fermata:

public static boolean DEBUG = true; //In some global class somewhere else 


... 

if (DEBUG) return; //in a completely unrelated class. 

... 

Poiché la costante DEBUG è completamente in-allineati, e funzionalmente equivalenti a semplicemente digitando un vero che se la condizione. Da una prospettiva JLS questi due casi sono molto simili.

13

Questo per consentire un tipo di compilazione condizionale.
Non è un errore con if, ma il compilatore segnala un errore per while, do-while e for.
Questo è OK:

if (true) return; // or false 
System.out.println("doing something"); 

Questo sono errori

while (true) { 
} 
System.out.println("unreachable"); 

while (false) { 
    System.out.println("unreachable"); 
} 

do { 
} while (true); 
System.out.println("unreachable"); 

for(;;) { 
} 
System.out.println("unreachable"); 

Viene spiegato alla fine del JLS 14.21: Unreachable Statements:

The rationale for this differing treatment is to allow programmers to define "flag variables" such as:

static final boolean DEBUG = false; 

and then write code such as:

if (DEBUG) { x=3; } 

The idea is that it should be possible to change the value of DEBUG from false to true or from true to false and then compile the code correctly with no other changes to the program text.

+2

+1 per la logica alla base della differenza di comportamento. –

+1

Questa è la risposta corretta. –

0

ho fatto un po 'di provare su Eclipse e pensare ci sono 3 tipi di gestione del codice morto di JDK: 1) nessun avviso, 2) avviso e 3) errore.

Per un tipico codice di compilazione condizionale "IF", JDK lo rileva e non lo segnala come codice morto. Per un codice morto causato da un flag booleano costante, JDK lo rileva e lo segnala a livello di avviso. Per il codice guasto causato dal flusso di controllo del programma, JDK lo rileva come errore.

Qui di seguito è la mia prova:

public class Setting { 
     public static final boolean FianlDebugFlag = false; 
    } 


    class B { 
    ..... 

    // no warn, it is typical "IF" conditional compilataion code 
    if(Setting.FianlDebugFlag) 
     System.out.println("am i dead?"); 
    if(false) 
     System.out.println("am i dead?"); 


    // warn, as the dead code is caused by a constant boolean flag 
    if(ret!=null && Setting.FianlDebugFlag) 
     System.out.println("am i dead?"); 

    if(Setting.FinalDebug)     
     return null;                
    System.out.println("am i dea?");   

    // error, as the dead code is due to the program's control flow 
    return null; 
    System.out.println("am i dead");   
    } 
0

Se si desidera ignorare l'avviso "codice morto avvertimento in Java sotto Eclipse" eseguire le seguenti operazioni all'interno di Eclipse *:

  1. Fare clic su Finestra-Preferenze Java-compiler-errori/avvisi
  2. Fare clic su "problemi di programmazione potenziali"
  3. scegliere "Ignora" la "Dead Codice esempio se (false)"
  4. Fare clic su Applica
  5. Fare clic su OK

Salvare e chiudere il vostro IDE Eclipse Quando si riapre eclissi, questi avvertimenti specifici non dovrebbero più essere incluso.

* Per questa soluzione esempio, sto utilizzando Eclipse IDE per sviluppatori Java - Versione: Mars.2 rilascio (4.5.2)

Problemi correlati