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

27

首先,如果这是一个重复的问题,我很抱歉。我发现了许多类似的问题,但没有直接回答我的问题。

为了准备即将到来的考试,我正在做一份过去的试卷。其中有一道题目给出了一段代码片段。我们必须判断它是否能够编译,如果不能,写出第一个编译器错误出现的行并解释它。这是代码片段:

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之外重新创建了这个文件,并通过命令行进行了编译/运行。它没有出现任何错误或警告,并且当我将鼠标悬停在文本框上时,显示了所需的java.awt.Point[x=...,y=...]

我的理解是匿名内部类可以访问:

  • 封闭类的字段
  • 封闭类的方法
  • 封闭作用域的局部变量,前提是它们是final

那么我错在哪里了?根据我所知道的,这段代码不应该工作

4个回答

33

我猜你正在使用Java 8编译。在这里,你的jtf变量是有效final的,所以它可以编译通过。如果一个变量在初始化后其值从未被更改,则它就是有效final的。

参见 本地类:

然而,在Java SE 8中,局部类可以访问封闭块的 final 或有效 final 的局部变量和参数。在初始化后其值从未更改的变量或参数即为有效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或更高版本时才能编译。


15
Java 8新增了访问"有效的final"变量的能力。只要变量在初始化后从未被更改,就不再需要使用final关键字。

1
它可能在Java8中起作用,因为重点在于“有效终态”,这意味着一旦将值分配给“jtf”,则其后不应更改。根据Java文档:
“变量或参数的值在初始化后永远不会更改,则有效地是最终的。”

0

看起来你的Eclipse IDE使用的是Java 7编译器。要将其更改为Java 8,请使用项目->属性->Java编译器->编译器兼容性级别。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接