2014-05-03 13 views
5

Supponiamo di avere un List<String> e un List<Transfomer>. Voglio applicare ogni trasformatore a ciascuna stringa nell'elenco.Java 8 lambda all'interno di una lambda non può modificare la variabile da lambda esterno

utilizzando Java 8 lambda, posso fare questo:

strings.stream().map(s -> { 
    for(Transformer t : transformers) { 
     s = t.apply(s); 
    } 
    return s; 
}).forEach(System.out::println); 

Ma mi piacerebbe fare qualcosa di più simile a questo, però il risultato e 'un errore di compilazione:

strings.stream().map(s -> transformers.stream().forEach(t -> s = t.apply(s))).forEach(System.out::println); 

I Sto solo iniziando a giocare con lambda, quindi forse non ho la sintassi corretta.

+1

Potrebbe essere utile includere il testo dell'errore del compilatore. – MatrixFrog

+1

Errore: (49, 60) java: le variabili locali a cui fa riferimento un'espressione lambda devono essere definitive o effettivamente definitive –

+0

Questa domanda è molto buona; tuttavia, suggerirei che dovrebbe essere spostato su Stack Overflow. –

risposta

13

Il modo migliore per farlo con i flussi è utilizzare reduce:

// make a transformer that combines all of them as one 
Transformer combinedTransformer = 

    // the stream of transformers 
    transformers.stream() 

    // combine all the transformers into one 
    .reduce(

     // apply each of the transformers in turn 
     (t1, t2) -> x -> t2.apply(t1.apply(x))) 

    ); 



// the stream of strings 
strings.stream() 

// transform each string with the combined transformer 
.map(combinedTranformer::apply); 

Naturalmente, ciò presuppone che transformers non è vuota; se v'è una possibilità che è vuota, che è abbastanza semplice da usare sovraccarico due argomenti di reduce invece, in questo modo (ciò presuppone Tranformer è un'interfaccia funzionale):

// make a transformer that combines all of them as one 
Transformer combinedTransformer = 

    // the stream of transformers 
    transformers.stream() 

    // combine all the transformers into one 
    .reduce(

     // the no-op transformer 
     x -> x, 

     // apply each of the transformers in turn 
     (t1, t2) -> x -> t2.apply(t1.apply(x))) 

    ); 



// the stream of strings 
strings.stream() 

// transform each string with the combined transformer 
.map(combinedTranformer::apply); 

La ragione hai un errore del compilatore è che, come dice l'errore, le variabili esterne usate in un'espressione lambda devono essere in modo efficace finale; ovvero, dichiarandoli final (se non lo sono già) non è necessario modificare il significato del programma, o cambiarne la compilazione. Pertanto, l'utilizzo di un'assegnazione mutabile in una lambda è generalmente vietato, e con buone ragioni: la mutazione aumenta la parallelizzazione e uno dei motivi principali per cui lambda era incluso in Java 8 era quello di consentire una programmazione parallela più semplice.

In generale, ogni volta che si desidera "sommare" i risultati in qualche modo, reduce (in uno dei suoi tre sovraccarichi) è il metodo go-to. Imparare a usare map, filter, reduce e flatMap è molto importante quando si lavora con Stream s.

+1

Grazie, è un bel po 'da accettare con la mia esperienza limitata con lambda. La sintassi è un po 'confusa, ma la esaminerò fino a quando non la capisco :) –

+1

+1, ma troverei più semplice leggere il codice se i commenti fossero più brevi o su righe separate in modo che non ci fosse scorrimento orizzontale. –

+0

@DavidConrad Come ti sembra ora? –

1

Lambdas (proprio come le classi locali) non può mai assegnare a variabili locali catturate, da un lambda esterno o da un metodo di inclusione. Le variabili locali catturate devono essere effettivamente definitive.