为什么 Lambda 表达式(匿名类) 不能访问非 final 类型的局部变量呢?
之前我一直认为是由于Lambda 表达式(匿名类) 会在另一个线程中执行,实例变量存在堆中,而局部变量是在栈上分配,如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了。
但是有人提出这个原因存在问题,说他们是在同一个线程中运行的,大兄弟提供的验证代码如下:
/**
* @author xxy
* @date 2021/4/13
*/
public class Java8Tester {
public static void main(String[] args) {
String str = "Hello, ";
Person person = name -> {
System.out.println(str + name + " " + Thread.currentThread().getId());
System.out.println("匿名类" + " " + Thread.currentThread().getId());
};
System.out.println("主方法" + " " + Thread.currentThread().getId());
person.say("JC");
}
interface Person {
void say(String name);
}
}
运行之后的结果显示确实都是main线程运行的,结果如下:
我又去看了一些其他的博客,有人说是Java为了防止数据不同步而规定的,也就是为了防止在lambda表达式内使用的外层局部变量被外层代码修改之后内部无法同步这个修改。
想看原贴可以点我
非常感谢这位老哥(@JC BLOG)的评论让我能够发现自己的错误,我觉得上面超链接中讲述的原因是比较好的,如果其他同学有不同意见欢迎一起交流一起进步。
Java 8 的 Lambda 可以捕获什么变量呢?
(1). 捕获实例变量或静态变量是没有限制的 (可认为是通过 final 类型的局部变量 this 来引用前两者);
(2). 捕获的局部变量必须显式的声明为 final 或实际效果的的 final 类型。
注意(敲黑板):如果在Lambda表达式中使用局部变量,即使我们没有声明成final类型的,编译器也会帮助我们将他们声明成final的,所以如果重新赋值会出错。
如下:
结果如下:
此时程序不会报错,虽然我们没有使用final修饰变量b,但是Lambda表达式中也能使用,这是因为编译器帮我们自动声明成final的。
众所周知,final类型的变量不能重新赋值,来验证下是不是编译器真的帮我们声明成了final,如下:
报错:
那么是什么时候帮我们声明成final呢?答案是在创建变量的时候直接声明成final的,测试如下:
总结:在Lambda表达式中可以捕获静态变量和实例变量,但是如果想要捕获局部变量的时候就需要声明成final的,即使我们不主动声明,编译器也会为我们自动声明成final的,不能再重新赋值。也就是Lambda表达式中访问的局部变量(隐式被声明为final的)是可读不可写的,但是Lambda表达式中访问的实例变量和静态变量是可读可写的。