It seems to me that you're getting confused between a variable being declared final
, and it being a constant.
The compiler doesn't replace all references to local variables with a constant - but when the instance of the anonymous class is constructed, the current value of each relevant variable is passed to the constructor, and stored in a variable within the anonymous class. That's just as fine for a parameter as for any other kind of local variable.
So this code:
public static void method(final int x) {
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(x);
}
};
r.run();
}
is broadly equivalent to:
public static void method(final int x) {
Runnable r = new AnonymousRunnable(x);
r.run();
}
private static class AnonymousRunnable implements Runnable {
private final int x;
AnonymousRunnable(int x) {
this.x = x;
}
@Override public void run() {
System.out.println(x);
}
}
I've made both the method and the nested class static to avoid worrying about whether this
is captured or not.
The local variable has to be final
when it's captured to avoid situations which could otherwise be confusing. Suppose that weren't the case - consider this example:
void method() {
int x = 10;
Runnable r = new Runnable() {
@Override public void run() {
System.out.println(x);
}
};
x = 20;
r.run(); // Should this print 10 or 20?
}
Using the current way that anonymous classes work, but just removing the final
restriction, it would print 10... but developers might expect it to print 20. Likewise, you should consider what would happen if you modified x
within the run
method. (As noted in Joop's answer, in Java 8 captured local variables are "effectively final" - so they act as if you've declared them to be final, but without doing so explicitly.)
As an example of a different approach, C# handles closures (for anonymous functions) in a different way, hoisting the local variables to a sort of anonymous class so that they can be modified. It's a more complex approach, but a bit more flexible. You might find my article about closures in Java and C# useful.