Lambda表达式值的作用范围
public static void main(String[] args) {
int limit = 10;
Runnable r = () -> {
int limit = 5;
for (int i = 0; i < limit; i++)
System.out.println(i);
};
}
和匿名内部类一样,不要在 Lambda 表达式主体内对方法内的局部变量进行修改,否则编译也不会通过:Lambda 表达式中使用的变量必须是 final 的。
这个问题发生的原因是因为 Java 规范中是这样规定的:
Any local variable, formal parameter, or exception parameter used but not declared in a lambda expression must either be declared final or be effectively final (§4.12.4)open in new window, or a compile-time error occurs where the use is attempted.
大致的意思就是说,Lambda 表达式中要用到的,但又未在 Lambda 表达式中声明的变量,必须声明为 final 或者是 effectively final,否则就会出现编译错误。
final和effectively final的区别
final int a;
a = 1;
// a = 2;
// 由于 a 是 final 的,所以不能被重新赋值
int b;
b = 1;
// b 此后再未更改
// b 就是 effectively final
int c;
c = 1;
// c 先被赋值为 1,随后又被重新赋值为 2
c = 2;
// c 就不是 effectively final
为什么需要定义为final呢?
我们都知道实例变量是储存在堆上面的,是线程贡献的。而局部变量则是保存在栈上的,是线程不共享的。
java 访问局部变量的时候,实际上是去访问他的副本。如果局部变量改变了,那访问的也是之前的值。尤其是当 Lambda 是在一个线程中使用变量的,造成的数据不同步问题更加明显,因此 Lambda 有了 final 限制。
在 Java 中方法调用是值传递的,所以在 lambda 表达式中对变量的操作都是基于原变量的副本,不会影响到原变量的值。
综上,假定没有要求 lambda 表达式外部变量为 final 修饰,那么开发者会误以为外部变量的值能够在 lambda 表达式中被改变,而这实际是不可能的,所以要求外部变量为 final 是在编译期以强制手段确保用户不会在 lambda 表达式中做修改原变量值的操作。(来自Google搜索)