3

我想知道在这种情况下 MouseAdapter 的范围是什么。

class foo extends JPanel()
{
  private JMenu edit = new JMenu();
  public foo()
  {
      this.edit.getItem(0).addMouseListener(new MouseAdapter(){ 
          @Override
          public void mouseClicked(MouseEvent e) {
              if (e.getClickCount() == 1) {
                  edit.getItem(0).setEnabled(true);
              }
          } 
      });
  }
}

我认为 MouseAdapter 可以访问变量edit因为新声明的 MouseAdapter 是类foo的内部类。但是,它找不到变量edit。如果我显式声明一个内部类并实现,例如 MouseAdapter 接口或其他接口,它可以从其中检测变量编辑。所以我的问题是new MouseAdpater()的范围是什么?此外,有人知道这方面的好书吗?非常感谢。顺便说一句,我得到的错误是局部变量是从内部类访问的,需要将其声明为 final

4

3 回答 3

3

要回答您的问题,您需要了解有关 JVM 是如何工作的基础知识。当编译包含内部类的类时,生成的字节码实际上并没有将内部类实现为类中的类。

为什么错误:局部变量是从内部类访问的,需要将其声明为 final

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JMenu;
import javax.swing.JPanel;

public class foo extends JPanel
{  
  public foo()
  {
    final JMenu edit = new JMenu();
    edit.getItem(0).addMouseListener(new MouseAdapter(){ 
    @Override
        public void mouseClicked(MouseEvent e) 
        {
            if (e.getClickCount() == 1) {
                edit.getItem(0).setEnabled(true);
            }
        } 
    });
  }
}

当你编译你的这个程序时,将创建两个文件,Foo.class 和 Foo$1.class。所以现在你的问题来了,因为Second类 iefoo$1.class不知道类ie中Variable edit存在。Firstfoo.class

那么如何解决这个问题呢?是什么JVM它要求开发人员将外部类的变量声明为 final

现在完成了,现在 JVM 悄悄地在第二个编译的类文件中放置了一个名为 val$edit 的隐藏变量,这是从javap

foo.class 的输出

C:\Mine\JAVA\J2SE\folder>javap foo.class
Compiled from "foo.java"
public class foo extends javax.swing.JPanel {
  public foo();
}

现在,编辑对于构造函数来说是本地的,因此输出如上。

C:\Mine\JAVA\J2SE\folder>javap foo$1.class
Compiled from "foo.java"
class foo$1 extends java.awt.event.MouseAdapter {
  final javax.swing.JMenu val$edit;
  final foo this$0;
  foo$1(foo, javax.swing.JMenu);
  public void mouseClicked(java.awt.event.MouseEvent);
}

val$edit 分配了与分配给 edit 相同的Variable值,因为现在编译器知道该值不能更改,因为它已被声明为 final,因此这次它可以工作。

现在,如果我将 the edit Variablefrom being更改为Localbeing怎么办Instance。现在类的对象知道关于这个变量的一切edit,如果它被改变了。所以同样改变上面的程序我们得到:

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JMenu;
import javax.swing.JPanel;

public class foo extends JPanel
{  
    JMenu edit = new JMenu();

    public foo()
    {   
        edit.getItem(0).addMouseListener(new MouseAdapter(){ 
        @Override
            public void mouseClicked(MouseEvent e) 
            {
            if (e.getClickCount() == 1) {
                    edit.getItem(0).setEnabled(true);
                }
            } 
        });
    }
}

在这种情况下,我们不假设将其声明和定义为 being final,因为在这种情况下,由于Variable对整个类是本地的,因此将与ieVariable一起发送到内部类Object Referencethis

C:\Mine\JAVA\J2SE\folder>javap foo.class
Compiled from "foo.java"
public class foo extends javax.swing.JPanel {
  javax.swing.JMenu edit;
  public foo();
}

这是Variable在这种情况下发送的方式,即 this$0 :

C:\Mine\JAVA\J2SE\folder>javap foo$1.class
Compiled from "foo.java"
class foo$1 extends java.awt.event.MouseAdapter {
  final foo this$0;
  foo$1(foo);
  public void mouseClicked(java.awt.event.MouseEvent);
}

据我说,这似乎是解释,这种情况是如何运作的。刚才我在互联网上找到了这个关于本地内部类可访问性之谜的精彩解释,可能会帮助您以更好的方式了解情况:-)

于 2012-04-01T15:51:16.443 回答
3

1)如果存在则edit.getItem(0)返回拳头JMenuItem,否则返回IllegalArgumentException

2) this.edit.getItem(0),而不是返回成员的类

3)edit.getItem(0).addMouseListener(new MouseAdapter(){是矛盾的,因为JMenuJMenuItem已经MouseEvents正确实施,为了更好的解决方法,你必须看看ButtonModel

4)没有理由scope of the mouse adapter

5) 用于监听来自JMenu(not JMenuItem)的事件MenuListener

于 2012-04-01T15:19:34.133 回答
1

如您所料,您的匿名内部类确实存在于其父对象的范围内。范围不是问题。

正如错误消息所暗示的那样,如果该成员被声明为最终成员,则匿名内部类只能访问其父级的“编辑”成员。

所以,改变

  private JMenu edit = new JMenu();

  private final JMenu edit = new JMenu();

它应该可以工作。

于 2012-04-01T15:42:12.737 回答