为什么在lambda表达式中使用的局部变量必须是final或有效final的?
为什么在lambda表达式中使用的局部变量必须是final或有效final的?
这个问题已经有了答案:
- 为什么只有final变量可以在匿名类中访问?
- 为什么实例变量在lambda表达式中使用时必须是final或有效final,而忽略“变量在lambda表达式中使用时必须是final或有效final”警告 [重复]?
- Lambda:局部变量需要final,实例变量不需要
但我读到了一篇文章(为什么Lambda中使用的局部变量必须是final或有效final?)。它举了一个例子:
Supplierincrementer(int start) { return () -> start++; }
这段代码会导致编译错误。这篇文章试图解释原因并说:
这个代码无法编译的基本原因是lambda捕获了start的值,也就是说创建了一个副本。强制变量为final可以避免让人误以为在lambda内部递增start实际上可以修改start方法参数。但是,为什么会创建副本呢?注意我们正在从方法中返回lambda。因此,在start方法参数被垃圾回收之后,lambda才会被执行。为了让这个lambda在方法之外继续存在,Java需要创建start的副本。
我无法理解它的解释,我有两个问题:
- "在start方法参数被垃圾回收之后,lambda才会被执行"是什么意思?
- 为什么要创建副本?
局部变量在lambda中使用必须是final或有效final的原因是,当从函数返回时,函数内部声明的变量和对象如果没有被“返回”,就会变为“超出范围”,程序员不能再访问它们。在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) { //变量a、b、c和e现在超出范围 //d由函数返回,仍然在使用中 String dReturned = enterNewScope("hello", "world"); }
函数Supplier<Integer> incrementer(int start) {...}
返回一个lambda,但是只有在函数已经返回后,你才能使用这个lambda,这意味着当你能够使用lambda时,变量 'start'已经超出范围。因此,Java会在调用lambda时复制你的 'start'变量,以防止无法访问它。
更多细节请参阅这个回答:Lambda Expression and Variable Capture
可以更准确地说,变量 'start'已经超出范围。无法确定start变量是否在内存中,可能强调一下,在Java中,lambda闭包的是值,而不是变量。
好的,编辑回答,试图让问题对提问者更清晰明了。
在Lambda表达式中使用的局部变量必须是final或有效final的原因是因为Lambda表达式的代码部分实际上是在调用Lambda所在方法的时候执行的,而不是在Lambda表达式被定义的时候执行。因此,局部变量在Lambda表达式中被引用时必须是final或有效final的,以确保它们在Lambda表达式执行时保持不变。
这个问题的解决方法已经在提供的第一个链接的答案中涵盖了。简而言之,可以遵循以下几个解决方法:
- 将局部变量声明为final或有效final,确保它们在Lambda表达式中不会被修改。
- 将局部变量转换为实例变量或类变量,以便在Lambda表达式中使用。
- 将局部变量封装成一个包含get和set方法的对象,以便在Lambda表达式中进行修改。
总之,为了在Lambda表达式中使用局部变量,必须将其声明为final或有效final,或者通过其他方式将其转换为可以在Lambda表达式中使用的形式。这样可以确保在Lambda表达式执行时,局部变量的值不会发生变化。