【问题标题】:Java - How am I accessing non final variables from an anonymous inner class?Java - 我如何从匿名内部类访问非最终变量?
【发布时间】:2012-05-10 13:57:53
【问题描述】:

正如您在以下代码中看到的,我正在从 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);

                }

            });

        }

【问题讨论】:

标签: java anonymous-inner-class


【解决方案1】:

如果变量在方法 def 中,那么它是一个 local 变量——并且您正在实例化一个在该方法执行后将存在的对象,以及该局部变量的生命周期,已经结束(它在方法的堆栈帧中,在方法返回时被销毁)。为了使其工作根本需要一些编译器魔法,这通常被称为 closure,即使它当前在 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 特有的限制,由于线程安全问题,您只能关闭最终变量。” - 实际上,真正的原因是非最终变量局部变量访问需要 JVM 支持闭包。外部方法的局部变量需要在其他方法调用结束后保持有效!!你不需要多个线程来触发这个......
  • 但是......他们确实仍然活着。在示例中,我们在每次执行操作时取消引用 e——远远超过返回的 myMethod
猜你喜欢
  • 2019-03-16
  • 1970-01-01
  • 1970-01-01
  • 2023-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多