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

时间:2015-05-09 15:28:16

标签: java java-8 inner-classes

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

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

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:未解决的编译问题:       不能引用封闭范围中定义的非最终局部变量jtf

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

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

  • 封闭类的字段
  • 封闭类的方法
  • 封闭范围的局部变量,前提是final

那我错过了什么?据我所知,此代码不应该

4 个答案:

答案 0 :(得分:33)

我猜你正在使用Java 8编译。这里你的jtf变量实际上是最终的,所以它编译得很好。如果变量初始化后它的值永远不会改变,那么变量实际上是最终的。

另见Local Classes

  

但是,从Java SE 8开始,本地类可以访问本地   封闭块的变量和参数是final或   有效地最后。变量或参数,其值永远不会   初始化后的更改实际上是最终的。

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

  

与本地类一样,匿名类可以捕获变量;他们有   对封闭范围的局部变量的相同访问:

     
      
  • 匿名类可以访问其封闭类的成员。

  •   
  • 匿名类无法访问其封闭的局部变量   未被宣布为最终或有效最终的范围。

  •   
     

[...]

如果你尝试过:

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 +时才会编译。

答案 1 :(得分:15)

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

答案 2 :(得分:1)

它可能在Java8中有效,因为压力在Effectively Final上,这意味着一旦将值分配给jtf,就不应该在病房之后更改它。根据Java doc:

  

变量或参数,其值永远不会更改   初始化实际上是最终的。

答案 3 :(得分:0)

似乎您的Eclipse IDE使用Java 7编译器。要将其更改为Java 8,请使用Project-> Properties-> Java Compiler-> Compiler compliance level。