Подтвердить что ты не робот

Ссылка на не конечную переменную: зачем этот код компилируется?

Прежде всего, я прошу прощения, если это дублирующий вопрос. Я нашел много подобных, но никто из них напрямую не затрагивает мой вопрос.

В рамках подготовки к предстоящему экзамену я делаю прошлую работу. У него есть вопрос, который дает фрагмент кода. Мы должны указать, если он компилируется, а если нет, напишите строку, в которой возникает первая ошибка компилятора, и объясните ее. Это фрагмент:

public static void main(String[] args) {
    JFrame f = new JFrame("hi");
    JTextField jtf = new JTextField(50);

    jtf.addMouseMotionListener(new MouseMotionAdapter() {
        public void mouseMoved(MouseEvent evt) {
            jtf.setText(evt.getLocationOnScreen().toString());
        }
    });

    f.add(jtf);
    f.setVisible(true);
}

Я ожидал, что он не будет компилироваться, поскольку jtf не final. Я протестировал свою теорию, введя код выше в Eclipse, который помечен ожидаемой ошибкой, но скомпилирован и работает нормально. Только после mousing над JTextField я получил ожидаемую ошибку:

java.lang.Error: проблема неразрешенной компиляции:     Нельзя ссылаться на не конечную локальную переменную jtf, определенную в охватывающей области

Я немного искал и обнаружил, что Eclipse использует собственную версию компилятора Java. Поэтому я переделал файл вне Eclipse и скомпилировал/выполнил его через командную строку. Он скомпилирован без ошибок или предупреждений, а когда мышь над текстовым полем, отобразите желаемый java.awt.Point[x=...,y=...].

Мое понимание анонимных внутренних классов заключается в том, что они могут получить доступ:

  • Поля охватывающего класса
  • Методы охватывающего класса
  • Локальные переменные охватывающей области, если они final

Так что мне не хватает? Согласно тому, что я знаю, этот код не должен работать.

4b9b3361

Ответ 1

Я предполагаю, что вы компилируете с Java 8. Здесь ваша переменная jtf является фактически окончательной, поэтому она компилируется отлично. Переменная эффективно является окончательной, если ее значение никогда не изменяется после ее инициализации.

См. также Локальные классы:

Однако, начиная с Java SE 8, локальный класс может обращаться к локальному переменные и параметры закрывающего блока, которые являются окончательными или фактически окончательный. Переменная или параметр, значение которого никогда измененный после его инициализации, является фактически окончательным.

и

Доступ к локальным переменным области охвата, а также объявление и доступ к членам анонимного класса

Как и локальные классы, анонимные классы могут захватывать переменные; они имеют тот же доступ к локальным переменным охватывающей области:

  • Анонимный класс имеет доступ к членам его вмещающего класса.

  • Анонимный класс не может получить доступ к локальным переменным в своем приложении которые не объявлены окончательными или фактически окончательными.

[...]

Если вы попытались:

javac -source 1.7 MyFile.java

у вас будет ожидаемая ошибка.

.java:13: error: local variable jtf is accessed from within inner class; needs to be declared final
                jtf.setText(evt.getLocationOnScreen().toString());
                ^
1 error

Таким образом, ответ на вопрос экзамена: он компилируется, только если вы используете Java 8 +.

Ответ 2

Java 8 добавила возможность доступа к "эффективно финальным" переменным. Ключевое слово final больше не требуется, если переменная никогда не изменяется после ее инициализации.

Ответ 3

Он может работать в Java8, поскольку напряжение находится на Effectively Final, что означает, что после присвоения значения jtf его нельзя изменять после настроек. В соответствии с Java doc:

Переменная или параметр, значение которого никогда не изменяется после того, как оно инициализированный фактически является окончательным.

Ответ 4

Кажется, что ваш Eclipse IDE использует компилятор Java 7. Чтобы изменить это на Java 8, используйте Project- > Properties- > Java Compiler- > Compiler.