【问题标题】:Why does the compiler deny access to non-final variables inside a lambda [duplicate]为什么编译器拒绝访问 lambda [重复] 中的非最终变量
【发布时间】:2017-03-03 04:08:15
【问题描述】:

我刚刚看到this question,显然Java 应该拒绝访问lambda 表达式主体内的非最终变量。为什么?

编辑:例如,我不明白为什么下面的代码是有害的:

String[] numbers = new String[10]; // put some numerical strings in
BigInteger sum = new BigInteger("0");
numbers.forEach(n -> sum = sum.add(new BigInteger(n)));

【问题讨论】:

  • 我认为如果你声明“本地非最终变量”更容易理解。例如。在方法中声明的变量。
  • @n247s 在方法里面?在 lambda 函数内部,还是在包含它的方法中?
  • 因为 lambda 的持续时间可能比封装它的块长。
  • @Jerfov2 lambda 是异步的吗?
  • @Bluefire 对不起,我不明白你的问题

标签: java lambda final


【解决方案1】:

Lambda 只是语法糖,它们被编译成anonymous inner classes。匿名内部类不能使用 non-final 局部变量,因为它们的范围。解释如下:

方法的局部变量存在于栈中,并且只存在 在方法的生命周期内。你已经知道a的范围 局部变量仅限于声明变量的方法。 当方法结束时,堆栈帧被吹走,变量 是历史。但即使在方法完成后,内部类 在其中创建的对象可能在堆上仍然存在,如果,对于 例如,对它的引用被传递到其他代码中,然后 存储在实例变量中。因为局部变量不是 只要方法局部的内部类对象就保证活着, 内部类对象不能使用它们。除非局部变量是 标记为决赛!

礼貌:Kathy Sierra 和 Bert Bates 的 SCJP 6 学习指南

【讨论】:

  • 缺乏这种解释。那为什么决赛没问题?当堆栈帧被销毁时,它们仍然会消失,不是吗?
  • 这是对匿名内部类的解释。放宽了 lambda 的规则
  • @puhlen:因为 lambda/匿名类实例/本地类实例的寿命比函数长,所以 lambda/匿名类实例/本地类实例在 lambda/匿名类实例/本地类实例被创建,并且 lambda/匿名类实例/本地类实例将使用它自己的变量副本。所以现在有两个独立的变量副本。如果它不是最终的,对一个副本的更改将不会反映在同一变量的另一个副本中。为了防止这种情况发生,它必须是最终的(或者在 Java 8 中实际上是最终的)。
【解决方案2】:

我不确定这是否是答案,但在 Java 的文档中,它声明如下:

对有效最终变量的限制禁止访问动态变化的局部变量,这些变量的捕获可能会引入并发问题。相比最终的限制,减少了程序员的文书负担。

我从中了解到的是,当你将 lambda 表达式传递给一个方法时,该表达式应该不会导致并发问题,因为该方法可以被多个线程并发使用,并且使用非 final 变量可能会导致竞速条件.

如果您想自己阅读并获取更多信息,请访问文档页面:http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.27.2

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多