正如您在以下代码中看到的,我正在从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);
}
});
}
答案 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]
的值更改为完全不同的值,并且方法这里显示的是观察到的变化,其中没有明确的变异代码。