正如您在下面的代码中看到的,我正在从 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);
}
});
}
最佳答案
如果变量位于方法 def 内部,那么它就是一个局部变量——并且您正在实例化一个在该方法执行后将存在的对象,以及该局部变量的生命周期,已结束(它位于方法的堆栈帧中,在方法返回时被销毁)。为了让它完全工作,需要一些编译器魔法,这通常被称为闭包,即使它在Java中的当前实现是如此蹩脚。编译器实际上会合成一个实现 ActionListener
的类,并具有一个实例变量,局部变量的值将复制到该实例变量中。
出于线程安全考虑,这是 Java 特有的限制,您只能关闭 final
变量。这里的故事是,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]
的值更改为完全不同的值,并且此处显示的方法将观察到该变化,而其中没有显式的突变代码。
关于Java - 如何从匿名内部类访问非最终变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10535567/