简体   繁体   English

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

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

Suppose I have a List<String> and a List<Transfomer> .假设我有一个List<String>和一个List<Transfomer> I want to apply each transformer to each string in the list.我想将每个转换器应用于列表中的每个字符串。

Using Java 8 lambdas, I can do this:使用 Java 8 lambdas,我可以这样做:

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.我刚刚开始使用 lambdas,所以也许我只是没有正确的语法。

The best way to do this with streams is to use reduce :使用流执行此操作的最佳方法是使用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);

Of course, this assumes that transformers is non-empty;当然,这假设transformers是非空的; 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):如果它有可能是空的,那么使用reduce的两个参数重载来代替它就足够简单了,就像这样(假设Tranformer是一个功能接口):

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

The reason you got a compiler error is that, as the error says, outside variables used in a lambda expression must be effectively final ;出现编译器错误的原因是,正如错误所说,lambda 表达式中使用的外部变量必须是有效的 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.也就是说,将它们声明为final (如果它们还没有)不能改变程序的含义,或者改变它是否编译。 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.因此,在 lambda 中使用可变赋值通常是被禁止的,并且有充分的理由:变异会破坏并行化,而 Java 8 中包含 lambda 的主要原因之一是允许更容易的并行编程。

Generally speaking, whenever you want to "sum up" results in some way, reduce (in any of its three overloads) is your go-to method.一般来说,每当您想以某种方式“总结”结果时, reduce (在其三个重载中的任何一个中)都是您的首选方法。 Learning how to use map , filter , reduce , and flatMap effectively is very important when working with Stream s.在使用Stream时,学习如何有效地使用mapfilterreduceflatMap非常重要。

Lambdas (just like local classes) cannot ever assign to captured local variables, whether from an outer lambda, or from an enclosing method. Lambdas(就像局部类一样)永远不能分配给捕获的局部变量,无论是来自外部 lambda 还是来自封闭方法。 Captured local variables must be effectively final.捕获的局部变量必须是有效的最终变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM