引用非final变量:为什么这段代码会编译?
首先,如果这是一个重复的问题,我道歉。 我找到了许多类似的,但没有一个直接解决我的问题。
为了准备即将到来的考试,我正在做一篇过去的论文。 它有一个提供代码段的问题。 我们必须声明它是否编译,如果不编译,则写入发生第一个编译器错误的行并解释它。 这是片段:
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中输入上面的代码来测试我的理论,它标记了预期的错误,但编译并运行得很好。 只有在将鼠标hover在JTextField
之后才会出现预期的错误:
java.lang.Error:未解决的编译问题:无法引用封闭范围中定义的非最终局部变量jtf
我做了一些搜索,发现Eclipse使用自己的Java编译器版本。 所以我在Eclipse之外重新创建了文件,并通过命令行编译/运行它。 它编译时没有错误或警告,当鼠标hover在文本字段上时,显示所需的java.awt.Point[x=...,y=...]
。
我对匿名内部类的理解是他们可以访问:
- 封闭类的字段
- 封闭类的方法
- 封闭范围的局部变量,只要它们是
final
那么我错过了什么? 据我所知,这段代码不适用 。
我猜你是用Java 8编译的。这里你的jtf
变量实际上是final,所以它编译得很好。 如果变量初始化后它的值永远不会改变,那么变量实际上是最终的。
另请参见本地类 :
但是,从Java SE 8开始,本地类可以访问最终或有效最终的封闭块的局部变量和参数。 在初始化之后其值永远不会改变的变量或参数实际上是最终的。
和
访问封闭范围的本地变量,以及声明和访问匿名类的成员
像本地类一样,匿名类可以捕获变量; 它们对封闭范围的局部变量具有相同的访问权限:
匿名类可以访问其封闭类的成员。
匿名类无法访问其封闭范围中未声明为final或者final final的局部变量。
[…]
如果您尝试过:
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+时才会编译。
Java 8增加了访问“有效最终”变量的能力。 只要变量在初始化后永远不会更改,就不再需要final
关键字。
它可以在Java8
工作,因为压力在于Effectively Final
,这意味着一旦将值分配给jtf
它就不应该在病房之后改变。 根据Java doc:
在初始化之后其值永远不会改变的变量或参数实际上是最终的。
似乎您的Eclipse IDE使用Java 7编译器。 要将其更改为Java 8,请使用Project-> Properties-> Java Compiler-> Compiler compliance level。