Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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:

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

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=...].

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.

like image 685
moarCoffee Avatar asked May 09 '15 15:05

moarCoffee


People also ask

How and why final variables are used?

Final variables can be used to construct trees of immutable objects. Once constructed, these objects are guaranteed not to change anymore. To achieve this, an immutable class must only have final fields, and these final fields may only have immutable types themselves.

Why declare a variable final Java?

A variable cannot be modified after it is declared as final. In other words, a final variable is constant. So, a final variable must be initialized and an error occurs if there is any attempt to change the value.

Can a local class have access to non final local variables Why?

A local inner class cannot be instantiated from outside the block where it is created in. Till JDK 7, the Local inner class can access only the final local variable of the enclosing block. However, From JDK 8, it is possible to access the non-final local variable of enclosing block in the local inner class.

What is known as final variable?

You can declare a variable in any scope to be final. . The value of a final variable cannot change after it has been initialized. Such variables are similar to constants in other programming languages.


2 Answers

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.

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.

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.

[...]

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+.

like image 107
Alexis C. Avatar answered Sep 19 '22 19:09

Alexis C.


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.

like image 42
John Kugelman Avatar answered Sep 19 '22 19:09

John Kugelman