I asked this question before but I didn't get an appropriate answer.
How can non-final fields be used in a anonymous class class if their value can change?
class Foo{
private int i;
void bar(){
i = 10
Runnable runnable = new Runnable (){
public void run (){
System.out.println(i); //works fine
}//end method run
}//end Runnable
}//end method bar
}//end class Foo
If the local variables which are used inside an anonymous class must be final
to enable the compiler inlining their values inside the anonymous class code like that:
Before:
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}//end method run
};//end anonymous class
}//end method f
}//end class Access1
After:
public class Access1 {
public Access1() {}//end constructor
public void f() {
Access1$1 access1$1 = new Access1$1(this);
}//end method f
}//end class Access1
And
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}//end constructor
public void run() {
System.out.println(3);
}//end method run
private final Access1 this$0;
}//end class Access1$1
Then how can the compiler inline a value of a non-final field?
There's a big difference between a method call's local variable (which must be
final
to be accessible to an inner class), and an instance's private data members.The inner class has access to the containing instance, and to all of the members of that instance,
final
or not. There's no need for them to be final, because they're referenced through (in your case)Foo.this
. So when accessing youri
member, the inner class is really accessingFoo.this.i
, it's just thatFoo.this
(likethis
) can be implied if a reference is unambiguous without it.But the anonymous class's code can't access local variables that way, because they aren't (of course) instance members of the containing class. So instead, the compiler does a very funny thing: It creates an instance member of the anonymous class for each
final
local variable, and when creating the instance of the anonymous class, it initializes those members with the values of the local variables.Let's watch it do that:
When compiled, we get
InnerEx.class
andInnerEx$1.class
. If we decompileInnerEx$1.class
, we see this:Note the instance member called
val$localVar
, which is the instance member created to stand in for the local variable in the call toInnerEx#test
.