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?<
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 your i
member, the inner class is really accessing Foo.this.i
, it's just that Foo.this
(like this
) 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:
public class InnerEx {
public static final void main(String[] args) {
new InnerEx().test("hi");
}
private void test(String arg) {
final String localVar = arg;
Runnable r = new Runnable() {
public void run() {
System.out.println(localVar);
}
};
r.run();
}
}
When compiled, we get InnerEx.class
and InnerEx$1.class
. If we decompile InnerEx$1.class
, we see this:
class InnerEx$1 implements java.lang.Runnable {
final java.lang.String val$localVar;
final InnerEx this$0;
InnerEx$1(InnerEx, java.lang.String);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LInnerEx;
5: aload_0
6: aload_2
7: putfield #2 // Field val$localVar:Ljava/lang/String;
10: aload_0
11: invokespecial #3 // Method java/lang/Object."<init>":()V
14: return
public void run();
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: getfield #2 // Field val$localVar:Ljava/lang/String;
7: invokevirtual #5 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
}
Note the instance member called val$localVar
, which is the instance member created to stand in for the local variable in the call to InnerEx#test
.