2015-08-28 18 views
5

Spiegami che in che modo l'espressione lambda può utilizzare e modificare la variabile di istanza della sua classe di inclusione e può utilizzare solo la variabile locale del suo ambito (a meno che non sia finale o finale effettivo)? La mia base domanda è come variabile della classe di istanza è modificabile all'interno di Lambda e variabile locale non è, in un contesto di portataEspressione lambda e cattura variabile

+2

Poiché lambda è effettivamente una classe interna anonima, questo è un possibile duplicato di [Perché solo le variabili finali sono accessibili nella classe anonima?] (Http://stackoverflow.com/questions/ 4732544/why-are-only-final-variables-accessible-in-anonimo-class) – mthmulders

+1

Questo è vero per Java 8, ma per quanto ne so non è specificato, quindi questo potrebbe essere cambiato nelle versioni future di Java. – Alex

+3

solo "questo" viene catturato. tutto l'accesso a una variabile di istanza all'interno del corpo lambda avviene tramite 'this.field', quindi puoi scrivere anche su di esso. – ZhongYu

risposta

8

In un primo momento, siamo in grado di dare uno sguardo al JLS, in cui si afferma quanto segue:

Qualsiasi variabile locale, parametro formale o parametro di eccezione utilizzato ma non dichiarato in un'espressione lambda deve essere dichiarato finale o essere effettivamente definitivo (§4.12.4), oppure si verifica un errore in fase di compilazione in cui viene tentato l'utilizzo.

Qualsiasi variabile locale utilizzata ma non dichiarata in un corpo lambda deve essere assegnata definitivamente (§16 (Assegnazione definita)) prima del corpo lambda o si verifica un errore in fase di compilazione.

Regole simili sull'uso variabile si applicano nel corpo di una classe interna (§8.1.3). La restrizione alle variabili finali effettivamente proibisce l'accesso a variabili locali che cambiano dinamicamente, la cui cattura potrebbe introdurre problemi di concorrenza. Rispetto alla restrizione finale, riduce il carico di lavoro dei programmatori.

La restrizione alle variabili finali effettive include variabili di ciclo standard, ma non potenziate per le variabili di ciclo, che vengono considerate distinte per ciascuna iterazione del ciclo (§14.14.2).


per capire meglio, dare un'occhiata a questo esempio di classe:

public class LambdaTest { 

    public static void main(String[] args) { 
     LambdaTest test = new LambdaTest(); 
     test.returnConsumer().accept("Hello"); 
     test.returnConsumerWithInstanceVariable().accept("Hello"); 
     test.returnConsumerWithLocalFinalVariable().accept("Hello"); 
    } 

    String string = " world!"; 

    Consumer<String> returnConsumer() { 
     return ((s) -> {System.out.println(s);}); 
    } 

    Consumer<String> returnConsumerWithInstanceVariable() { 
     return ((s) -> {System.out.println(s + string);}); 
    } 

    Consumer<String> returnConsumerWithLocalFinalVariable() { 
     final String foo = " you there!"; 
     return ((s) -> {System.out.println(s + foo);}); 
    } 

} 

uscita di principale è

Hello 
Hello world! 
Hello you there! 

Questo perché la restituzione di un lambda qui è più o meno la come creare una nuova classe anonima con new Consumer<String>() {...}. Il tuo lambda - un'istanza di Consumer<String> ha un riferimento alla classe in cui è stato creato. Puoi riscrivere la variabile returnConsumerWithInstanceVariable per utilizzare System.out.println(s + LambdaTest.this.string), questo farebbe esattamente lo stesso. Questo è il motivo per cui è consentito accedere (e modificare) variabili di istanza.

Se nel metodo è presente una variabile locale finale (effettiva), è possibile accedervi poiché è possibile accedervi poiché viene copiata nell'istanza lambda.

Ma, tuttavia, se non definitivo, cosa pensi che dovrebbe accadere nel contesto seguente:

Consumer<String> returnConsumerBad() { 
    String foo = " you there!"; 
    Consumer<String> results = ((s) -> {System.out.println(s + foo);}); 
    foo = " to all of you!"; 
    return results; 
} 

Qualora il valore vengono copiati per l'istanza, ma poi non si aggiorna quando la variabile locale è aggiornato? Ciò probabilmente causerebbe confusione, poiché penso che molti programmatori si aspettassero di avere il nuovo valore "per tutti voi" dopo aver restituito questo lambda.

Se si dispone di un valore primitivo, si posiziona in pila. Quindi non puoi semplicemente fare riferimento alla variabile locale, perché potrebbe scomparire dopo che è stata raggiunta la fine del tuo metodo.

0

È possibile consultare questo articolo - https://www.infoq.com/articles/Java-8-Lambdas-A-Peek-Under-the-Hood spiegato sulla compilazione di espressioni lambda.Come spiegato le espressioni lambda/i blocchi di codice sono compilati nella classe anonima, queste classi anonime sono compilate con il formato del nome (<<Enclosing Class name>>$<<1(Number)>>), quindi supponiamo di assumere se non sono consentite variabili locali non definitive, quindi il compilatore non può rintracciare da dove questa variabile locale è riferita come classe anonima i file .class vengono creati/compilati con il formato sopracitato separatamente come le normali classi java.

Quindi se la variabile locale è definitiva, il compilatore crea un'istanza finale nella classe anoymous che non crea ambiguità al compilatore. Per ulteriori informazioni, fare riferimento al collegamento menzionato