在封闭范围内定义的局部变量日志必须是最终的或实际上是最终的

新手上路,请多包涵

我是 lambda 和 Java8 的新手。我面临以下错误。

在封闭范围内定义的局部变量日志必须是最终的或实际上是最终的

public JavaRDD<String> modify(JavaRDD<String> filteredRdd) {

    filteredRdd.map(log -> {

        placeHolder.forEach(text -> {

            //error comes here
            log = log.replace(text, ",");

        });

        return log;

    });

    return null;
}

原文由 Balaji Reddy 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 454
2 个回答

该消息准确说明了问题所在:您的变量 log 必须是最终的(即:携带关键字 final)或实际上是最终的(即:您只在 lambda 之外为其赋值 _一次_)。否则,您不能在 lambda 语句中使用该变量。

但是,当然,这与您对 log 的使用相冲突。关键是:你不能从 lambda 内部写入外部的东西……所以你必须退后一步并寻找其他方式来完成你打算做的事情。

从这个意义上说:只要相信编译器。

除此之外,还有一个 核心 点需要理解:你 不能 使用你可以写入的局部变量。局部变量在运行时被“复制”到 lambda 的上下文中,为了实现确定性行为,它们只能被读取,并且应该是 常量

如果您的用例是 写入 某个对象,那么它应该是您的封闭类的一个字段!

所以,长话短说:

  • 在 lambda 中使用(读取)的 局部 变量必须像 常量 一样
  • 你不能 局部变量!
  • 或者反过来:如果你需要写一些东西,你必须使用你周围类的一个字段(或者提供一个回调方法)

原文由 GhostCat 发布,翻译遵循 CC BY-SA 4.0 许可协议

这种限制 的原因与 Java 语言特性的原因相同,即从(匿名)内部类内部访问的 局部变量 必须(有效地)是 final

rgettman 的 这个答案 详细介绍了它。 rgettman 详细解释了这些限制,我链接到那个答案,因为 lambda 表达式的行为应该与匿名内部类的行为相同。但是请注意,类或实例变量不存在此类限制。这样做的主要原因有点复杂,我没有比 Roedy Green 在 这里 做的更好的解释了。仅在此处复制,以便它在一个地方:

规则是匿名内部类只能访问封闭方法的最终局部变量。为什么?因为内部类的方法可能稍后被调用,在产生它的方法终止很久之后,例如通过 AWT(高级窗口工具包)事件。局部变量早已不复存在。然后,匿名类必须使用它需要的那些快速冻结的副本,这些副本被编译器秘密地存储在匿名内部类对象中。你可能会问,为什么局部变量必须是 final 的?编译器难道不能像处理非最终参数那样获取非最终局部变量的副本吗?如果这样做,您将拥有该变量的两个副本。每个都可以独立更改,就像调用者和被调用者的参数副本一样,但是您将使用相同的语法来访问任一副本。这会令人困惑。所以孙坚持当地是最终的。这使得它实际上有两个副本无关紧要。

匿名类访问调用者的最终局部变量的能力实际上只是自动传递一些局部变量作为额外构造函数参数的语法糖。整件事对我来说都是稀释的 eau de kludge 的味道。

原文由 Kedar Mhaswade 发布,翻译遵循 CC BY-SA 3.0 许可协议

推荐问题