внутри JButton требуются конечные переменные, выполняемые действием?

Итак, у меня есть JList, и я пытаюсь использовать его внутри метода JButtons actionPerformed, и он просит меня сделать JList final, почему это так, ниже фрагмент кода

public SomeClass() {    
  btnNewButton.addActionListener(new ActionListener() {
       public void actionPerformed(ActionEvent e) {
         list.clearSelection();             
    }});
}

На самом деле у меня нет проблем с тем, чтобы сделать его окончательным, я просто не уверен, зачем мне это нужно.


person yahh    schedule 03.11.2012    source источник
comment
Об этом вы можете прочитать в здесь   -  person CAMOBAP    schedule 03.11.2012


Ответы (3)


Чтобы ответить на ваш вопрос, вам необходимо понять основы работы 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, так это то, что он требует от разработчика объявить переменную внешнего класса как 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);
}

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

Что, если я изменю 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);
}

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

person nIcE cOw    schedule 03.11.2012
comment
Я бы предложил +10 за это, если бы мог. Отличное объяснение. - person Paul Samsotha; 11.07.2014

Вы создаете анонимный класс, а объем переменной list слишком ограничен, поэтому вы не можете получить к нему доступ. Если только вы не сделаете это final или не сделаете его полем SomeClass.

Дополнительную информацию см. В этой статье.

person Robin    schedule 03.11.2012

Сделав list переменной как final, они больше не являются ссылками на переменные, а являются константами. Затем компилятор может просто заменить использование list в анонимном классе значениями констант (конечно, во время компиляции), и у вас больше не будет проблем с доступом к несуществующим переменным.

person CAMOBAP    schedule 03.11.2012