Java 8 lambda within a lambda can't modify variable from outer lambda

在Java 8λλ不能修改变量从外λ

Tags: java lambda
标签: java lambda

问题 (Question)

Suppose I have a List<String> and a List<Transfomer>. I want to apply each transformer to each string in the list.

Using Java 8 lambdas, I can do this:

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

But I'd like to do something more like this, however it results in a compile time error:

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

I'm just starting to play with lambdas, so maybe I just don't have the syntax correctly.

假如我有一List<String>和一个List<Transfomer>。我想申请每个变压器中的每个字符串列表。

使用Java 8的lambda,我能做这个:

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

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

最佳答案 (Best Answer)

The best way to do this with streams is to use reduce:

Transformer combinedTransformer = // make a transformer that combines all of them as one
    transformers.stream() // the stream of transformers
    .reduce( // combine all the transformers into one
        (t1, t2) -> x -> t2.apply(t1.apply(x))) // apply each of the transformers in turn
    );

strings.stream() // the stream of strings
.map(combindedTranformer::apply); // transform each string with the combined transformer

Of course, this assumes that transformers is non-empty; if there is a possibility that it is empty, than it is simple enough to use the two-argument overload of reduce instead, like so (this assumes Tranformer is a functional interface):

Transformer combinedTransformer = // make a transformer that combines all of them
    transformers.stream() // the stream of transformers
    .reduce( // combine all the transformers into one
        x -> x, // the no-op transformer
        (t1, t2) -> x -> t2.apply(t1.apply(x))) // apply each of the transformers in turn
    );

strings.stream() // the stream of strings
.map(combindedTranformer::apply); // transform each string with the combined transformer

The reason you got a compiler error is that, as the error says, outside variables used in a lambda expression must be effectively final; that is, declaring them final (if they aren't already) must not change the meaning of the program, or change whether or not it compiles. Using a mutable assignment in a lambda is therefore generally forbidden, and with good reason: mutation screws up parallelization, and one of the major reasons lambdas were included in Java 8 was to allow easier parallel programming.

Generally speaking, whenever you want to "sum up" results in some way, reduce (in any of its three overloads) is your go-to method. Learning how to use map, filter, reduce, and flatMap effectively is very important when working with Streams.

相反,像这样(这个假设reduce是一个功能接口):

Transformer combinedTransformer = // make a transformer that combines all of them as one
    transformers.stream() // the stream of transformers
    .reduce( // combine all the transformers into one
        (t1, t2) -> x -> t2.apply(t1.apply(x))) // apply each of the transformers in turn
    );

strings.stream() // the stream of strings
.map(combindedTranformer::apply); // transform each string with the combined transformer

你有一个编译错误的原因是,作为错误说,外部变量使用lambda表达式必须transformers有效的最终reduce有效的最终Tranformer那是,宣布他们;

Transformer combinedTransformer = // make a transformer that combines all of them
    transformers.stream() // the stream of transformers
    .reduce( // combine all the transformers into one
        x -> x, // the no-op transformer
        (t1, t2) -> x -> t2.apply(t1.apply(x))) // apply each of the transformers in turn
    );

strings.stream() // the stream of strings
.map(combindedTranformer::apply); // transform each string with the combined transformer

(如果不是已经)不改变程序的意义,或改变它是否编译。使用一个可变的分配在一个lambda是因此通常是被禁止的,并有很好的理由:突变螺丝的并行化,和一个lambda的主要原因包括在Java 8是可以非常容易的并行编程。使用一个可变的分配在一个lambda是因此通常是被禁止的,并有很好的理由:突变螺丝的并行化,和一个lambda的主要原因包括在Java 8是可以非常容易的并行编程。final一般来说,当你想“总结”的一些结果,(在其任何三个重载)是你的方法。

学习如何使用reduce学习如何使用,mapfilter,和reduce,和flatMap有效是非常重要的工作时,StreamS.

本文翻译自StackoverFlow,英语好的童鞋可直接参考原文:http://stackoverflow.com/questions/23451579