为什么lambda表达式使用的局部变量要是final的
为什么 Lambda 表达式(匿名类) 不能访问非 final 的局部变量呢?
因为实例变量存在堆中,而局部变量是在栈上分配,Lambda 表达(匿名类) 会在另一个线程中执行。如果在线程中要直接访问一个局部变量,可能线程执行时该局部变量已经被销毁了,而 final 类型的局部变量在 Lambda 表达式(匿名类) 中其实是局部变量的一个拷贝,因为之前的存放在栈中(lambda表达式在其他线程运行的时候可能栈已经释放了),所以必须要拷贝出一份来。
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表达式中访问的实例变量和静态变量是可读可写的。