2014-05-03 6 views
5

Angenommen, ich habe eine List<String> und eine List<Transfomer>. Ich möchte jeden Transformator auf jede Zeichenfolge in der Liste anwenden.Java 8 Lambda innerhalb eines Lambda kann Variable von äußeren Lambda nicht ändern

mit Java 8 lambdas, ich kann dies tun:

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

Aber ich möchte etwas mehr wie dies tun, jedoch kommt es zu einem Fehler der Kompilierung:

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

I fange gerade an, mit lambdas zu spielen, also habe ich gerade die syntax nicht richtig.

+1

Es könnte helfen, den Text des Compiler-Fehlers einzuschließen. – MatrixFrog

+1

Fehler: (49, 60) Java: Lokale Variablen, die von einem Lambda-Ausdruck referenziert werden, müssen endgültig oder effektiv sein. –

+0

Diese Frage ist sehr gut; Ich würde jedoch vorschlagen, dass es zu Stack Overflow verschoben werden sollte. –

Antwort

13

Der beste Weg, dies mit Strömen zu tun ist, reduce zu verwenden:

// 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); 

Natürlich setzt dies voraus, dass transformers nicht leer ist; Wenn es eine Möglichkeit ist, dass es leer ist, als es einfach genug ist, um die Zwei-Argument Überlastung der reduce stattdessen zu verwenden, wie so (unter Annahme Tranformer eine funktionelle Schnittstelle):

// 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); 

Der Grund, Sie haben einen Compiler Fehler ist, dass, wie der Fehler sagt, externe Variablen in einem Lambda-Ausdruck verwendet werden müssen effektiv endgültigen; das heißt, sie zu deklarieren final (wenn sie nicht bereits sind) darf nicht die Bedeutung des Programms ändern oder ändern, ob es kompiliert wird. Die Verwendung einer veränderbaren Zuweisung in einem Lambda ist daher generell verboten, und das aus gutem Grund: Die Mutation verschraubt die Parallelisierung, und einer der Hauptgründe, warum lambdas in Java 8 enthalten war, war eine einfachere parallele Programmierung.

Im Allgemeinen, wann immer Sie Ergebnisse in irgendeiner Weise "zusammenfassen" möchten, ist reduce (in jeder seiner drei Überladungen) Ihre Go-to-Methode. Lernen, wie man map, filter, reduce und flatMap effektiv verwendet, ist sehr wichtig, wenn man mit Stream s arbeitet.

+1

Danke, das ist ziemlich viel, um mit meiner begrenzten Erfahrung mit Lambdas aufzunehmen. Die Syntax ist ein wenig verwirrend, aber ich werde es durchgehen, bis ich es verstehe :) –

+1

+1, aber ich würde es leichter finden, den Code zu lesen, wenn die Kommentare kürzer oder in separaten Zeilen waren, so dass es nicht war horizontales Scrollen –

+0

@DavidConrad Wie sieht das jetzt aus? –

1

Lambdas (genau wie lokale Klassen) können gefangenen lokalen Variablen niemals zuweisen, weder von einem äußeren Lambda noch von einer umschließenden Methode. Erfasste lokale Variablen müssen effektiv final sein.