【问题标题】:Why do local variables used in lambdas have to be final or effectively final? [duplicate]为什么 lambda 中使用的局部变量必须是最终的或有效的最终变量? [复制]
【发布时间】:2019-10-23 17:09:39
【问题描述】:

这个问题在这里已经有了答案:

但我读了一篇文章(Why Do Local Variables Used in Lambdas Have to Be Final or Effectively Final?)。它发布了一个示例:

Supplier<Integer> incrementer(int start) {
  return () -> start++;
}

代码将转换编译错误。帖子试图解释原因并说:

这不会编译的基本原因是 lambda 正在捕获 start 的值,这意味着制作它的副本。强制变量为 final 可以避免给人一种印象,即在 lambda 中递增 start 实际上会修改 start 方法参数。 但是,为什么要复制呢?好吧,请注意我们正在从我们的方法中返回 lambda。因此,在 start 方法参数被垃圾回收之后,lambda 才会运行。 Java 必须复制 start 才能使该 lambda 存在于该方法之外。

我无法理解它的解释,我有两个问题:

  1. “直到 start 方法参数被垃圾回收后 lambda 才会运行”是什么意思?
  2. 为什么要复制?

【问题讨论】:

  • 当您不理解该解释时,为什么不忽略它并使用您承认的三个问答之一来回答您的问题?

标签: java java-8


【解决方案1】:
  1. 这意味着 lambda 代码(start++ 部分)在调用 Supplier 对象的 get() 方法之前不会被执行,这发生在 incrementer() 方法的调用者的某个地方,即一段时间incrementer() 方法返回之后。

    写这句话的人大错特错,因为参数在堆栈上,而不是在堆上,所以它们不会被“垃圾收集”。鉴于这样的巨大错误,我建议您忽略该帖子,因为作者不知道他在做什么。

  2. 您提供的第一个链接的accepted answer 已经涵盖了这一点。我建议你阅读它。

【讨论】:

    【解决方案2】:

    当您从函数返回时,在函数内声明的未“返回”的变量和对象成为“超出范围”并且程序员不应再访问,在 java 的情况下,这些对象最终会被删除由垃圾收集器。

    public String enterNewScope(String a, String b) {
        String c = a + b;
        String d = b + c;
        String e = c + d;
    
        return d;
    }
    
    public static void main(String...args) {
        //The variables a, b, c, and e are now out of scope
        //d is returned by the function so it's still in use.
        String dReturned = enterNewScope("hello", "world");
    }
    

    函数Supplier&lt;Integer&gt; incrementer(int start) {...} 返回一个 lambda,但您只能在函数返回后使用 lambda,这意味着只要您能够使用该 lambda,变量“start”就超出了范围。因此,Java 会复制您的“开始”变量,以防在您调用 lambda 时无法访问它。

    查看其他答案了解更多详情:Lambda Expression and Variable Capture

    【讨论】:

    • 更准确地说是变量“开始”超出范围。开始变量可能在内存中,也可能不在内存中,它真的不知道。也许强调在 java lambdas 中关闭值,而不是变量。
    • @NathanHughes 好点,编辑了答案以尝试明确操作。
    猜你喜欢
    • 2017-11-01
    • 2015-01-25
    • 1970-01-01
    • 2022-11-13
    • 1970-01-01
    • 2017-08-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多