The difference is between local 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.
Final local variables
There are two reasons I know for making a local variable or a
parameter final. The first reason is that you don't want your code
changing the local variable or parameter. It is considered by many to
be bad style to change a parameter inside a method as it makes the
code unclear. As a habit, some programmers make all their parameters
"final" to prevent themselves from changing them. I don't do that,
since I find it makes my method signature a bit ugly.
The second reason comes in when we want to access a local variable or
parameter from within an inner class. This is the actual reason, as
far as I know, that final local variables and parameters were
introduced into the Java language in JDK 1.1.
public class Access1 {
public void f() {
final int i = 3;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(i);
}
};
}
}
Inside the run() method we can only access i if we make it final in the outer class. To understand the reasoning, we have to
look at what the compiler does. It produces two files, Access1.class
and Access1$1.class. When we decompile them with JAD, we get:
public class Access1 {
public Access1() {}
public void f() {
Access1$1 access1$1 = new Access1$1(this);
}
}
and
class Access1$1 implements Runnable {
Access1$1(Access1 access1) {
this$0 = access1;
}
public void run() {
System.out.println(3);
}
private final Access1 this$0;
}
Since the value of i is final, the compiler can "inline" it into the inner
class. It perturbed me that the local variables had to be final to be
accessed by the inner class until I saw the above.
When the value of the local variable can change for different
instances of the inner class, the compiler adds it as a data member of
the inner class and lets it be initialised in the constructor. The
underlying reason behind this is that Java does not have pointers, the
way that C has.
Consider the following class:
public class Access2 {
public void f() {
for (int i=0; i<10; i++) {
final int value = i;
Runnable runnable = new Runnable() {
public void run() {
System.out.println(value);
}
};
}
}
}
The problem here is that we have to make a new local data member each time we go through the for loop, so a thought I had today
while coding, was to change the above code to the following:
public class Access3 {
public void f() {
Runnable[] runners = new Runnable[10];
for (final int[] i={0}; i[0]<runners.length; i[0]++) {
runners[i[0]] = new Runnable() {
private int counter = i[0];
public void run() {
System.out.println(counter);
}
};
}
for (int i=0; i<runners.length; i++)
runners[i].run();
}
public static void main(String[] args) {
new Access3().f();
}
}
We now don't have to declare an additional final local variable. In fact, is it not perhaps true that
int[] i is like a common C pointer to an int? It took me 4 years to
see this, but I'd like to hear from you if you have heard this idea
somewhere else.