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

时间:2021-11-23 23:28:58

First off, I apologise if this is a duplicate question. I found many similar ones, but none that directly address my question.

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

In preparation for an upcoming exam, I am doing a past paper. It has a question that gives a code snippet. We have to state if it compiles, and if not, write the line at which the first compiler error occurs and explain it. This is the snippet:

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

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);
}

I was expecting it not to compile as jtf is not final. I tested my theory by entering the code above in Eclipse, which flagged the expected error, but compiled and ran just fine. It was only after mousing over the JTextField that I got the expected error:

我期待它不能编译,因为jtf不是最终的。我通过在Eclipse中输入上面的代码来测试我的理论,它标记了预期的错误,但编译并运行得很好。只有在将鼠标悬停在JTextField之后才会出现预期的错误:

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

java.lang.Error:未解决的编译问题:无法引用封闭范围中定义的非最终局部变量jtf

I did a bit of searching, and discovered that Eclipse uses its own version of the Java compiler. So I remade the file outside of Eclipse and compiled/ran it via the command line. It compiled with no errors or warnings, and when mousing over the text field, displayed the desired java.awt.Point[x=...,y=...].

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

My understanding of anonymous inner classes is that they can access:

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

  • Fields of the enclosing class
  • 封闭类的字段

  • Methods of the enclosing class
  • 封闭类的方法

  • Local variables of the enclosing scope, provided they are final
  • 封闭范围的局部变量,只要它们是最终的

So what am I missing? According to what I know, this code should not work.

那么我错过了什么?据我所知,这段代码不适用。

4 个解决方案

#1


33  

I guess you are compiling with Java 8. Here your jtf variable is effectively final, so it compiles fine. A variable is effectively final if its value is never changed after you initialized it.

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

See also 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.

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

and

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.

    匿名类无法访问其封闭范围中未声明为final或者final final的局部变量。

[...]

If you tried with:

如果您尝试过:

javac -source 1.7 MyFile.java

you'll have your expected error.

你会得到预期的错误。

.java:13: error: local variable jtf is accessed from within inner class; needs to be declared final
                jtf.setText(evt.getLocationOnScreen().toString());
                ^
1 error

So the answer of the exam question is: it compiles only if you're using Java 8+.

因此,考试问题的答案是:只有在使用Java 8+时才会编译。

#2


15  

Java 8 added the ability to access "effectively final" variables. The final keyword is no longer required as long as a variable is never changed after it is initialized.

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

#3


1  

It may work in Java8 as the stress is on Effectively Final which means once the value is assigned to jtf it should not be changed after wards. As per Java doc:

它可以在Java8中工作,因为压力在于有效最终,这意味着一旦将值分配给jtf,它就不应该在病房之后改变。根据Java doc:

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

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

#4


0  

Seems that your Eclipse IDE uses Java 7 compiler. To change this to Java 8 use Project->Properties->Java Compiler->Compiler compliance level.

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

#1


33  

I guess you are compiling with Java 8. Here your jtf variable is effectively final, so it compiles fine. A variable is effectively final if its value is never changed after you initialized it.

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

See also 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.

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

and

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.

    匿名类无法访问其封闭范围中未声明为final或者final final的局部变量。

[...]

If you tried with:

如果您尝试过:

javac -source 1.7 MyFile.java

you'll have your expected error.

你会得到预期的错误。

.java:13: error: local variable jtf is accessed from within inner class; needs to be declared final
                jtf.setText(evt.getLocationOnScreen().toString());
                ^
1 error

So the answer of the exam question is: it compiles only if you're using Java 8+.

因此,考试问题的答案是:只有在使用Java 8+时才会编译。

#2


15  

Java 8 added the ability to access "effectively final" variables. The final keyword is no longer required as long as a variable is never changed after it is initialized.

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

#3


1  

It may work in Java8 as the stress is on Effectively Final which means once the value is assigned to jtf it should not be changed after wards. As per Java doc:

它可以在Java8中工作,因为压力在于有效最终,这意味着一旦将值分配给jtf,它就不应该在病房之后改变。根据Java doc:

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

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

#4


0  

Seems that your Eclipse IDE uses Java 7 compiler. To change this to Java 8 use Project->Properties->Java Compiler->Compiler compliance level.

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