引用非最终变量:为什么这段代码可以编译?

Referencing non-final variable: why does this code compile?

首先,如果这是一个重复的问题,我深表歉意。我发现了很多类似的,但是 none 直接解决了我的问题。

为了准备即将到来的考试,我正在做一份过去的论文。它有一个给出代码片段的问题。我们必须说明它是否编译,如果没有,写下第一个编译器错误发生的行并解释它。这是片段:

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 中输入上面的代码来测试我的理论,它标记了预期的错误,但是编译并且 运行 就好了。只有在将鼠标悬停在 JTextField 上之后,我才收到预期的错误:

java.lang.Error: Unresolved compilation problem: Cannot refer to the non-final local variable jtf defined in an enclosing scope

我做了一些搜索,发现 Eclipse 使用它自己版本的 Java 编译器。因此,我在 Eclipse 之外重新制作了该文件,并通过命令行 compiled/ran 它。它编译时没有错误或警告,当鼠标悬停在文本字段上时,显示了所需的 java.awt.Point[x=...,y=...].

我对匿名内部classes的理解是他们可以访问:

那我错过了什么?据我所知,这段代码 应该行不通 .

Java 8 添加了访问 "effectively final" 变量的能力。只要变量在初始化后永不更改,就不再需要 final 关键字。

我猜你正在用 Java 8 编译。这里你的 jtf 变量实际上是最终的,所以它编译得很好。如果变量的值在您初始化后从未更改,则该变量实际上是最终的。

另见 Local Classes:

However, starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final.

Accessing Local Variables of the Enclosing Scope, and Declaring and Accessing Members of the Anonymous Class

Like local classes, anonymous classes can capture variables; they have the same access to local variables of the enclosing scope:

  • An anonymous class has access to the members of its enclosing class.

  • An anonymous class cannot access local variables in its enclosing scope that are not declared as final or effectively 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+ 时才能编译。

它可能在 Java8 中工作,因为压力在 Effectively Final 上,这意味着一旦将值分配给 jtf,它就不应在以后更改。根据 Java 文档:

A variable or parameter whose value is never changed after it is initialized is effectively final.

您的 Eclipse IDE 似乎使用 Java 7 编译器。要将其更改为 Java8,请使用项目->属性->Java 编译器->编译器合规性级别。