Ссылка на не конечную переменную: зачем этот код компилируется?
Прежде всего, я прошу прощения, если это дублирующий вопрос. Я нашел много подобных, но никто из них напрямую не затрагивает мой вопрос.
В рамках подготовки к предстоящему экзамену я делаю прошлую работу. У него есть вопрос, который дает фрагмент кода. Мы должны указать, если он компилируется, а если нет, напишите строку, в которой возникает первая ошибка компилятора, и объясните ее. Это фрагмент:
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
Так что мне не хватает? Согласно тому, что я знаю, этот код не должен работать.
Ответы
Ответ 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.