lambda中的Java 8 lambda无法修改外部lambda中的变量

假设我有一个List和一个List 。 我想将每个变换器应用于列表中的每个字符串。

使用Java 8 lambdas,我可以这样做:

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

但是我想做更像这样的事情,但是它会导致编译时错误:

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

我刚开始玩lambdas,所以也许我只是没有正确的语法。

使用streams执行此操作的最佳方法是使用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); 

当然,这假设transformers是非空的; 如果它有可能是空的,那么简单地使用reduce的双参数重载就好了,就像这样(这假设Tranformer是一个function接口):

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

你遇到编译器错误的原因是,正如错误所说,lambda表达式中使用的外部变量必须是有效的 ; 也就是说,声明它们是final (如果它们还没有)不得改变程序的含义,或改变它是否编译。 因此,通常禁止在lambda中使用可变赋值,并且有充分理由:突变会导致并行化,并且Java 8中包含lambdas的主要原因之一是允许更容易的并行编程。

一般来说,每当你想以某种方式“总结”结果时, reduce (在它的三个重载中的任何一个)是你的首选方法。 在使用Stream时,学习如何有效地使用mapfilterreduceflatMap非常重要。

Lambdas(就像本地类一样)无法分配给捕获的局部变量,无论是来自外部lambda还是来自封闭方法。 捕获的局部变量必须是有效的最终变量。