2015-05-09 7 views
27

Prima di tutto, mi scuso se si tratta di una domanda doppia. Ne ho trovati molti simili, ma nessuno che riguardasse direttamente la mia domanda.Riferimento alla variabile non finale: perché questo codice viene compilato?

In preparazione per un esame imminente, sto facendo un passato. Ha una domanda che fornisce uno snippet di codice. Dobbiamo specificare se compila e, in caso contrario, scrivere la riga in cui si verifica il primo errore del compilatore e spiegarlo. Questo è il frammento:

public static void main(String[] args) { 
    JFrame f = new JFrame("hi"); 
    JTextField jtf = new JTextField(50); 

    jtf.addMouseMotionListener(new MouseMotionAdapter() { 
     public void mouseMoved(MouseEvent evt) { 
      jtf.setText(evt.getLocationOnScreen().toString()); 
     } 
    }); 

    f.add(jtf); 
    f.setVisible(true); 
} 

mi aspettavo di non compilare come jtf non è final. Ho testato la mia teoria inserendo il codice sopra in Eclipse, che ha contrassegnato l'errore previsto, ma compilato e eseguito bene. Fu solo dopo il mouse sopra JTextField che ho avuto l'errore previsto:

java.lang.Error: Unresolved compilation problem: Cannot refer to the non-final local variable jtf defined in an enclosing scope

ho fatto un po 'di ricerca, e ha scoperto che Eclipse utilizza una propria versione del compilatore Java. Così ho rifatto il file all'esterno di Eclipse e lo ho compilato/eseguito tramite la riga di comando. Compilato senza errori o avvisi, e quando si passava il mouse sul campo di testo, visualizzava lo java.awt.Point[x=...,y=...] desiderato.

mia comprensione di classi interne anonime è che possono accedere a:

  • Campi di classe contenitrice
  • I metodi della classe racchiude
  • variabili locali del campo di applicazione che racchiude, a condizione che siano final

Quindi cosa mi manca? Secondo quanto noto, questo codice non dovrebbe funzionare.

risposta

33

Immagino che tu stia compilando con Java 8. Qui la tua variabile jtf è effettivamente definitiva, quindi compila bene. Una variabile è effettivamente definitiva se il suo valore non viene mai modificato dopo averlo inizializzato.

Vedi anche Local Classes:

However, starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final.

e

Accessing Local Variables of the Enclosing Scope, and Declaring and Accessing Members of the Anonymous Class

Like local classes, anonymous classes can capture variables; they have the same access to local variables of the enclosing scope:

  • An anonymous class has access to the members of its enclosing class.

  • An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively final.

[...]

Se si è tentato con:

javac -source 1.7 MyFile.java 

avrai il tuo ex errore pect.

.java:13: error: local variable jtf is accessed from within inner class; needs to be declared final 
       jtf.setText(evt.getLocationOnScreen().toString()); 
       ^
1 error 

Quindi la risposta della domanda d'esame è: si compila solo se stai usando Java 8+.

15

Java 8 ha aggiunto la possibilità di accedere alle variabili "effectively final". La parola chiave final non è più necessaria finché una variabile non viene mai modificata dopo l'inizializzazione.

1

Può funzionare in Java8 poiché lo stress è su Effectively Final il che significa che una volta assegnato il valore a jtf non deve essere modificato dopo i reparti.Come per Java doc:

A variable or parameter whose value is never changed after it is initialized is effectively final.

0

Sembra che l'IDE Eclipse utilizzi il compilatore Java 7. Per cambiarlo in Java 8 utilizzare Progetto-> Proprietà-> Compilatore Java-> Livello di conformità del compilatore.