封闭作用域中定义的局部变量日志必须是最终的或实际上是最终的

2022-09-01 04:20:36

我是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;
}

答案 1

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

但是,当然,这与您对日志的使用相冲突。关键是:你不能从lambda内部写入外部的东西......所以你必须退后一步,为你打算做什么寻找其他方法。

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

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

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

所以,长话短说:

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

答案 2

此限制的原因与 Java 语言功能的原因相同,即从(匿名)内部类中访问的局部变量必须(有效地)是最终的

rgettman的这个答案深入到它的细节。rgettman 清楚地详细解释了这些限制,我链接到这个答案,因为 lambda 表达式的行为应该与匿名内部类的行为相同。但是,请注意,对于类或实例变量,不存在此类限制。造成这种情况的主要原因有点复杂,我无法比Roedy Green在这里所做的更好地解释它。仅在此处复制,使其位于一个位置:

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

匿名类访问调用方的最终局部变量的能力实际上只是语法糖,用于自动传入一些局部变量作为额外的构造函数参数。整个事情对我来说闻起来像是稀释的油。


推荐