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

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

                }

            });

        }

最佳答案

如果变量位于方法 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/

相关文章:

java - Cobertura ant 脚本缺少 Log4J 类

Java Canvas 类不作画

android - LeakCanary 通过 locationlistener 的匿名实现报告泄漏的 Activity 实例

java - 实例化通用接口(interface)

java - 我如何从匿名类访问我的主类?

java - 设置 Jersey 时出错

java - 抛出 IllegalArgumentException 不起作用

java - 如何使用 Mule ESB 在 smtp 中添加多个邮件附件

java - 特定于枚举常量的类主体是静态的还是非静态的?

java - 从java中的内部类访问变量