область действия адаптера мыши - PullRequest
3 голосов
/ 01 апреля 2012

Мне интересно, какова область действия 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 или что-то еще, он может обнаружить внутри него переменную edit . Поэтому мой вопрос в том, какова область действия new MouseAdpater () имеет? Кроме того, кто-нибудь знает хорошее чтение по этому вопросу? Большое спасибо. Кстати, я получил ошибку локальная переменная была доступна из внутреннего класса, необходимо объявить ее окончательно

Ответы [ 3 ]

3 голосов
/ 01 апреля 2012

Чтобы ответить на ваш вопрос, вам необходимо понять основы того, как JVM использует для работы. Когда классы компилируются, которые содержат внутренние классы, генерируемый байтовый код фактически не реализует внутренние классы как класс внутри класса.

ПОЧЕМУ ОШИБКА: к локальной переменной обращались из внутреннего класса, необходимо объявить ее окончательно

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, т.е. foo$1.class не знает, что Variable edit присутствует внутри класса First, т.е. foo.class.

Так как решить эту проблему? JVM делает то, что Требуется, чтобы разработчик объявил переменную внешнего класса как окончательную .

Теперь, когда это сделано, JVM незаметно помещает скрытую переменную с именем val $ edit во 2-й скомпилированный файл класса, вот вывод, полученный из javap

Ouput для 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);
}

Для Variable val $ edit назначено то же значение, которое было назначено для редактирования, поскольку теперь компилятор знает, что это значение нельзя изменить, поскольку оно было объявлено как final, и, следовательно, оно работает на этот раз.

А что если я поменяю edit Variable с Local на 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);
                }
            } 
        });
    }
}

Здесь, в этом случае, мы не должны объявлять и определять его как final, потому что в этом случае, поскольку Variable является локальным для всего класса, Variable отправляется во внутренний класс с Object Reference т.е. this

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 отправляется в этом случае, то есть это $ 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);
}

По-моему, это интерпретация того, как работает эта ситуация. Только что я нашел в интернете это замечательное объяснение относительно Тайны доступности в местных внутренних классах , может быть, это поможет вам лучше понять ситуацию: -)

3 голосов
/ 01 апреля 2012

1) edit.getItem(0) возвращает кулак JMenuItem, если существует, в противном случае возвращает IllegalArgumentException

2) this.edit.getItem(0), а не класс, который возвращает члены

3) edit.getItem(0).addMouseListener(new MouseAdapter(){контрапродуктивно, потому что JMenu, JMenuItem правильно реализовали MouseEvents, для лучшего обходного пути вы должны взглянуть на ButtonModel

4) нет причин для scope of the mouse adapter

5) для прослушивания событий из JMenu (не JMenuItem) посмотрите на MenuListener

1 голос
/ 01 апреля 2012

Ваш анонимный внутренний класс, как вы и ожидаете, существует в области действия своего родительского объекта. Сфера не проблема.

Как следует из сообщения об ошибке, анонимный внутренний класс может получить доступ к члену "edit" своего родителя, только если этот член объявлен как final.

Итак, измени

  private JMenu edit = new JMenu();

до

  private final JMenu edit = new JMenu();

и оно должно работать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...