Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why a non-final "local" variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can?

Tags:

java

There are some topics on Stack Overflow on the compiler error Cannot refer to a non-final variable message inside an inner class defined in a different method and the solution is "declare it as final and you're done", but with this theoretical question I would like to inspect what is the logical reason why this code cannot compile:

private void updateStatus(String message) {     Runnable doUpdateStatus = new Runnable() {          public void run() {              /* do something with message */          }     }     /* do something with doUpdateStatus, like SwingUtilities.invokeLater() */ } 

(solution: declare message as final) whereas this one does:

private String enclosingClassField; private void updateStatus() {     Runnable doUpdateStatus = new Runnable() {          public void run() {              /* do something with enclosingClassField */          }     }     /* do something with doUpdateStatus, like SwingUtilities.invokeLater() */ } 

I'm really confused. enclosingClassField is not final, it can change every time many times, whereas the poor message argument of updateStatus can only change within its method body, and is instead blamed by the compiler ;)

Even the compiler error is misleading to me. Cannot refer to a non-final variable message inside an inner class defined in a different method: Different from what? Isn't message defined in the same method as the inner class? Isn't enclosingClassField instead defined outside the method? Uhm...

Can someone point me to the correct interpretation of this matter? Thanks.

like image 638
gd1 Avatar asked Apr 27 '11 09:04

gd1


People also ask

Can we use Non final local variables inside a local inner class?

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.

Can an inner class declared inside of a method access local variables of this method?

Yes, we can access the local final variables using the method local inner class because the final variables are stored on the heap and live as long as the method local inner class object may live.

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

Accessing Members of an Enclosing Class In addition, a local class has access to local variables. However, a local class can only access local variables that are declared final.

Can local inner class access non final local variables Mcq?

A local class can only access local variables that are declared final in java.


2 Answers

The difference is between local (method) variables vs class member variables. A member variable exists during the lifetime of the enclosing object, so it can be referenced by the inner class instance. A local variable, however, exists only during the method invocation, and is handled differently by the compiler, in that an implicit copy of it is generated as the member of the inner class. Without declaring the local variable final, one could change it, leading to subtle errors due to the inner class still referring to the original value of that variable.

Update: The Java Specialists' Newsletter #25 discusses this in more detail.

Even the compiler error is misleading to me. Cannot refer to a non-final variable message inside an inner class defined in a different method: Different from what?

From the inner class' run method I believe.

like image 57
Péter Török Avatar answered Oct 12 '22 00:10

Péter Török


The reason is that Java doesn't support closures. There are no JVM commands to access local variable from outside the method, whereas fields of class can be easily accessed from any place.

So, when you use final local variable in an inner class, compiler actually passes a value of that variable into constructor of the inner class. Obviously, it won't work for non-final variables, since they value can change after construction of the inner class.

Fields of containing class don't have this problem, because compiler implicitly passes a reference to the containing class into the constructor of the inner class, thus you can access its fields in a normal way, as you access fields of any other class.

like image 22
axtavt Avatar answered Oct 12 '22 01:10

axtavt