这个问题在这里已有答案:
-
How can non-final fields be used in a anonymous class class if their value can change? 1回答
-
Lambdas: local variables need final, instance variables don't 9个答案
-
Why can an anonymous class access non-final class member of the enclosing class 4个答案
这个问题类似于Lambdas: local variables need final, instance variables don't,但唯一的区别是这个问题即使没有lambda表达式也是有效的,即使在Java7上也是有效的 .
这是下面的代码片段 .
公共类MyClass {
Integer globalInteger = new Integer(1);
public void someMethod() {
Integer localInt = new Integer(2);
Runnable runnable = new Runnable() {
@Override
public void run() {
globalInteger = new Integer(11);//no error
localInt = new Integer(22);//error here
}
};
}
}
我被允许为globalInteger重新分配一个新值,但不允许给localInteger . 为什么会有这种差异?
4 回答
编译器告诉你内部类中的变量必须是最终的或有效的最终错误 . 这是由于Java的8封闭以及JVM如何捕获引用 . 限制是在lambda体中捕获的引用必须是final(不可重新分配),并且编译器需要确保它不引用局部变量的副本 .
所以如果你访问实例变量,你的lambda实际上是引用周围类的
this
实例,这是有效的final(非变化引用) . 此外,如果使用包装类或数组,编译器错误也会消失 .因为JVM没有机器指令来分配位于任何堆栈帧的变量,该变量不同于当前堆栈帧 .
要理解为什么允许非局部变量改变,我们首先需要理解为什么局部变量不是 . 这是因为局部变量存储在堆栈中(实例(或静态)变量不存在) .
堆栈变量的问题在于它们显然很糟糕,通过复制实现对局部变量的访问 . 也就是说,类使用的所有局部变量(包括特殊变量
this
)都被复制到匿名对象中 . 因此,当内部类的方法访问局部变量x
时,它会访问存储在对象中的副本's not actually accessing that local variable. It' .但是如果在创建对象之后局部变量发生了变化,或者对象的方法改变了变量,会发生什么?好吧,前者会导致局部变量发生变化,而不会导致对象中的副本发生变化,后者会更改副本,但不会更改原始副本 . 因此,无论哪种方式,变量的两个版本将不再相同,这对任何不了解正在进行复制的程序员来说都是非常直观的 . 因此,为避免此问题,只允许访问本地变量(如果它们的值永远不会更改) .
实例变量不会消失,直到它们的包含对象被垃圾收集(并且静态变量永远不会消失) - 因为匿名对象将包含对外部
this
的引用,这会被复制't happen until the anonymous object is garbage collected as well. So since they aren' t,修改它们并没有理由不允许它 .因为lambda函数不是类的一部分 .
考虑以下代码(您的小改动):