Java - 如何从匿名内部类访问非最终变量?

时间:2012-05-10 13:57:53

标签: java anonymous-inner-class

正如您在以下代码中看到的,我正在从ActionListener匿名内部类访问JLabel。这给了我没有错误,所以如何允许这样做但是如果JLabel是INSIDE,那么在没有最终修饰符的情况下不允许使用该方法?

JLabel e = new JLabel("");
        public void myMethod() {

            JButton b = new JButton("ok");
            b.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent arg0) {
                    e.setSize(200,200);

                }

            });

        }

1 个答案:

答案 0 :(得分:3)

如果变量在方法def中,那么它是本地变量 - 并且您正在实例化该方法执行后将存在的对象,并使用它该局部变量的生命周期已经结束(它在方法的堆栈框架中,在方法返回时被破坏)。要使其工作完全,需要一些编译器魔法,这通常称为闭包,即使它在Java中的当前实现是如此蹩脚。编译器实际上将合成一个实现ActionListener的类,并具有一个实例变量,本地变量的值将被复制到该实例变量中。

这是一项特定于Java的限制,由于线程安全问题,您只能关闭final vars。这里的故事是,在Java开发人员的直觉中根深蒂固的是,本地var总是线程安全的 - 它不能在方法的执行过程中发生变化(没有明确地通过如果你可以关闭非最终变量,因为闭包可以与当前正在执行的方法并行执行并更改该变量,那么这将违反。这会导致一些非常违反直觉的行为。

然而,有一种(有点蹩脚)的解决方法:

public void myMethod() {
    final JLabel[] e = {new JLabel("")};
    JButton b = new JButton("ok");
    b.addActionListener(new ActionListener() {
       @Override public void actionPerformed(ActionEvent arg0) {
         e[0].setSize(200,200);
    }});
}

e现在是一个单元素数组,它驻留在堆上,并且不会被方法的堆栈框架破坏。您也可以运用自己的直觉来确定它现在肯定线程安全,并且侦听器可以轻松地将e[0]的值更改为完全不同的值,并且方法这里显示的是观察到的变化,其中没有明确的变异代码。