2014-07-15 17 views
12

Sto tentando di ridefinire questo codice per utilizzare una lambda anziché una classe anonima. È un semplice elenco di elementi in una GUI. Ho registrato un ascoltatore diverso per ogni elemento e l'ultimo elemento creato fa qualcosa di speciale quando si fa clic.Il codice si comporta in modo diverso dopo la conversione della classe anonima in lambda

class ItemList { 
    interface OnClickListener { 
     void onClick(); 
    } 
    OnClickListener current; 
    OnClickListener newListener(final int n) { 
     return current = new OnClickListener() { 
      public void onClick() { 
       if (this == current) 
        System.out.println("doing something special (item #"+n+")"); 
       System.out.println("selected item #" + n); 
      } 
     }; 
    } 
    public static void main(String[] args) { 
     ItemList l = new ItemList(); 
     OnClickListener ocl1 = l.newListener(1); 
     OnClickListener ocl2 = l.newListener(2); 
     OnClickListener ocl3 = l.newListener(3); 
     ocl1.onClick(); 
     ocl2.onClick(); 
     ocl3.onClick(); 
    } 
} 

Esso funziona come previsto:

$ javac ItemList.java && java ItemList 
selected item #1 
selected item #2 
doing something special (item #3) 
selected item #3 

Ora posso cambiare in modo da utilizzare un lambda al posto di classe anonima:

class ItemList { 
    interface OnClickListener { 
     void onClick(); 
    } 
    OnClickListener current; 
    OnClickListener newListener(final int n) { 
     return current =() -> { 
      if (this == current) 
       System.out.println("doing something special (item #"+n+")"); 
      System.out.println("selected item #" + n); 
     }; 
    } 
    public static void main(String[] args) { 
     ItemList l = new ItemList(); 
     OnClickListener ocl1 = l.newListener(1); 
     OnClickListener ocl2 = l.newListener(2); 
     OnClickListener ocl3 = l.newListener(3); 
     ocl1.onClick(); 
     ocl2.onClick(); 
     ocl3.onClick(); 
    } 
} 

Ma ora non è più fa nulla di speciale l'ultimo articolo? Perché? == funziona in modo diverso con lambda? All'inizio pensavo che fosse un errore in retrolambda, ma questo esempio è in esecuzione su JDK8 semplice e succede ancora.

$ javac A.java && java A 
selected item #1 
selected item #2 
selected item #3 
+1

Lambdas non può realmente riferirsi a 'this' nel loro corpo. –

risposta

12

this all'interno lambda non significa la stessa di this all'interno di un'istanza di classe anonima.

All'interno di un lambda si riferisce alla classe di chiusura.

... l'espressione lambda non introduce un nuovo livello di scoping. Di conseguenza, è possibile accedere direttamente ai campi, ai metodi e alle variabili locali dello scope che racchiude. ... Per accedere alle variabili nella classe di inclusione, utilizzare la parola chiave this. ...

All'interno un'istanza di una classe anonima, si riferisce all'oggetto corrente

Da un metodo di istanza o un costruttore, questo è un riferimento all'oggetto corrente - l'oggetto di cui metodo o il costruttore viene chiamato

Ecco perché nell'espressione lambda, this == current non è mai vero, dal momento che confronta un'istanza della classe ItemList con un'espressione lambda di tipo OnClickListener.

+0

Ancora non è chiaro perché corrente == questo non è mai vero. Dato che racchiudere la classe è la stessa, dovremmo vedere tre "qualcosa" nell'output. –

+0

@OlegGryb La classe di inclusione è ItemList, quindi non è mai uguale a corrente (che è l'espressione lambda di tipo OnClickListener). – Eran

+0

Hai ragione, non ci ho pensato, quindi current.onClick() dovrebbe funzionare anche se voglio usarlo in questo modo. –