2013-08-02 11 views
14

Mi sono imbattuto in un comportamento, non sapevo prima, nel codice di follow-up.Differenza di comportamento: membro statico finale inizializzato 'null' e variabile locale finale inizializzata 'null'

consideri il 1 ° caso:

public static void main(String[] args) {  
    final String str = null; 
    System.out.println(str.length()); // Compiler Warning: NullPointerAccess 
} 

Come previsto, il compilatore mi mostra seguente avvertenza str essere nulla-Null accesso puntatore: La str variabile può essere solo nulla a questo Posizione.

Ora, quando mi sono trasferito quella variabile un statica campo finale inizializzato a nulla:

class Demo { 
    static final String str = null; 

    public static void main(String[] args) { 
     System.out.println(str.length()); // No Compiler Warning 
    } 
} 

Ora, compilatore non risulta alcun preavviso. AFAIK, il compilatore dovrebbe sapere che str è definitivo, non cambierà il suo valore, in qualsiasi punto del codice. E dato che è null, sicuramente si tradurrà in NullPointerException in seguito, cosa che fa.

Sebbene, nel primo caso, il compilatore mi avverta correttamente di questo, perché non può identificarlo nel secondo caso. Perché questo cambiamento di comportamento? Il comportamento è lo stesso, se cambio il campo static nel campo instance e accedendo utilizzando un'istanza di Demo.

Ho pensato che questo comportamento potrebbe essere stato specificato in JLS, quindi ho esaminato l'argomento Assegnazione definita, ma non ho trovato nulla relativo a questo problema. Qualcuno può spiegare il cambiamento di comportamento? Sto cercando qualche punto di forza con qualche link a JLS se possibile?

A parte questo, il motivo per cui il compilatore mi mostra solo avvertimento in primo luogo, come credo per lo stesso motivo ho detto sopra, la chiamata di metodo sarà sicuramente buttare NPE in fase di esecuzione, dal momento che il campo può' essere cambiato? Perché non mi ha mostrato un errore del compilatore? Mi aspetto troppo dal compilatore, come sembra abbastanza ovvio, che il risultato di runtime di str.length() non può essere diverso da NPE?


Scusate mancante che in precedenza:

sto usando Eclipse Juno, sulla Ubuntu 12.04 con OpenJDK 7.

+7

Il JLS non * di solito * entra nei dettagli degli avvisi molto - è un dettaglio dell'implementazione del compilatore.Non sarei sorpreso se alcuni compilatori * lo facessero * su questo –

+0

@JonSkeet. qualsiasi compilatore si comporti nello stesso modo in entrambi i casi.In entrambi i casi, sto accedendo al metodo su riferimento null definito –

+0

Sì, sarebbe bello farlo - ma sto dicendo che è una questione di come di gran lunga l'attuazione compilatore vuole andare. non mi aspetto che sia nella JLS, e non dico quale compilatore si sta usando. –

risposta

2

Wow! Si è scoperto che era un problema specifico di Eclipse. Quando ho compilato il codice utilizzando:

javac -Xlint:all Demo.java 

non ha mostrato alcun avvertimento per qualsiasi evenienza. Così, sono tornato su eclissi per verificare le impostazioni abilitate per questo caso e ne ho trovato uno.

In di Windows ->Preferenze ->Java ->compilatore ->errori/avvisi, sotto Null Analisi, posso cambiare il modo in cui il Null Pointer accesso dovrebbe essere trattato da Eclipse Compiler. Posso impostarlo su - Ignora, Errore o Avviso.

e ora sembra come una questione del tutto stupida. Mi vergogno. :(

1
public static void main(String[] args) {  
    final String str = null; 
    System.out.println(str.length()); // Compiler Warning: NullPointerAccess 
} 

In questo caso str è una variabile locale e il compilatore non verrà compilato se si tenta di eseguire qualsiasi operazione su di esso prima di inizializzarlo. L'analisi del flusso è completamente diversa, controllerà il flusso del codice in cui rileva che nel momento in cui si esegue l'operazione length() lo str della variabile locale può essere solo null.

class Demo { 
    static final String str = null; 

    public static void main(String[] args) { 
     System.out.println(str.length()); // No Compiler Warning 
    } 
} 

In questo caso str è una variabile di istanza e sarà nullo, anche se non in modo esplicito assegna così.

Ancora perché nessun avvertimento qui?

Si potrebbe avere inizializzare la variabile di istanza nel costruttore. Oppure puoi chiamare un metodo setter su di esso prima di chiamare l'operazione lenght() su di esso. Quindi sfugge all'analisi del flusso (il compilatore non è sicuro se la variabile di istanza sarà nullo in quel punto o meno, ma nel primo caso la compilazione è sicura che la variabile locale sarà sempre nullo).

+0

avrei buyed nel caso di campi non definitive, nel qual caso, il comportamento è destinato a essere diverso in due casi. Ma dato che entrambe sono variabili finali, nessuna di esse può essere cambiata dopo l'inizializzazione. Quindi, non riesco a vedere come il compilatore non può identificare l'accesso nullo in un caso, mentre può nell'altro. –

+1

Penso che tu non abbia notato che il campo è statico finale. Non puoi inizializzarlo nel costruttore, né in qualsiasi setter? –

+0

Sì, non ho visto la finale lì!Non so esattamente come funziona il rilevamento dei flussi in Java, ma suppongo che non gestisca il tuo caso. Se non fosse statico e solo definitivo, potrebbe essere inizializzato nel costruttore e se fosse statico in qualche blocco statico. Il tuo caso è unico e immagino che non sia gestito. –

2

Non sono sicuro nel 100%, ma nel secondo caso in cui si ha il campo finale contro la variabile locale, è possibile assegnare a questo campo finale un valore diretto in statico (o un blocco di istanza dipendente se la variabile è statica o no) blocco di inizializzazione:

class Demo { 
... 
static { 
str = "some text"; 
} 
... 
} 

in modo che il compilatore non ti avvisi.

+1

Non se è già inizializzato. –