0

正如您在以下代码中看到的,我正在从 ActionListener 匿名内部类访问 JLabel。这为我提供了没有错误,所以这是如何允许的,但是如果 JLabel 是 INSIDE,那么没有 final 修饰符就不允许该方法?

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);

                }

            });

        }
4

1 回答 1

3

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

final这是 Java 特有的限制,由于线程安全问题,您只能关闭var。这里的故事是,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]完全不同的值,并且此处显示的方法将观察到该更改,而其中没有显式的突变代码。

于 2012-05-10T13:59:04.067 回答