首页 文章

为什么java允许在匿名内部类中重新分配类级变量,而局部变量不允许相同[duplicate]

提问于
浏览
1

这个问题在这里已有答案:

这个问题类似于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 回答

  • 3

    编译器告诉你内部类中的变量必须是最终的或有效的最终错误 . 这是由于Java的8封闭以及JVM如何捕获引用 . 限制是在lambda体中捕获的引用必须是final(不可重新分配),并且编译器需要确保它不引用局部变量的副本 .

    所以如果你访问实例变量,你的lambda实际上是引用周围类的 this 实例,这是有效的final(非变化引用) . 此外,如果使用包装类或数组,编译器错误也会消失 .

  • 1

    因为JVM没有机器指令来分配位于任何堆栈帧的变量,该变量不同于当前堆栈帧 .

  • 0

    要理解为什么允许非局部变量改变,我们首先需要理解为什么局部变量不是 . 这是因为局部变量存储在堆栈中(实例(或静态)变量不存在) .

    堆栈变量的问题在于它们显然很糟糕,通过复制实现对局部变量的访问 . 也就是说,类使用的所有局部变量(包括特殊变量 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,修改它们并没有理由不允许它 .

  • 0

    因为lambda函数不是类的一部分 .

    考虑以下代码(您的小改动):

    public Runnable func() {
        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
            }
        };
        return runnable;
    }
    
    //Somewhere in the code:
    Runnable r = func();
    r.run(); // At this point localInt is not defined.
    

相关问题