Why Are Only Final Variables Accessible in Anonymous Class

Why are only final variables accessible in anonymous class?

As noted in comments, some of this becomes irrelevant in Java 8, where final can be implicit. Only an effectively final variable can be used in an anonymous inner class or lambda expression though.


It's basically due to the way Java manages closures.

When you create an instance of an anonymous inner class, any variables which are used within that class have their values copied in via the autogenerated constructor. This avoids the compiler having to autogenerate various extra types to hold the logical state of the "local variables", as for example the C# compiler does... (When C# captures a variable in an anonymous function, it really captures the variable - the closure can update the variable in a way which is seen by the main body of the method, and vice versa.)

As the value has been copied into the instance of the anonymous inner class, it would look odd if the variable could be modified by the rest of the method - you could have code which appeared to be working with an out-of-date variable (because that's effectively what would be happening... you'd be working with a copy taken at a different time). Likewise if you could make changes within the anonymous inner class, developers might expect those changes to be visible within the body of the enclosing method.

Making the variable final removes all these possibilities - as the value can't be changed at all, you don't need to worry about whether such changes will be visible. The only ways to allow the method and the anonymous inner class see each other's changes is to use a mutable type of some description. This could be the enclosing class itself, an array, a mutable wrapper type... anything like that. Basically it's a bit like communicating between one method and another: changes made to the parameters of one method aren't seen by its caller, but changes made to the objects referred to by the parameters are seen.

If you're interested in a more detailed comparison between Java and C# closures, I have an article which goes into it further. I wanted to focus on the Java side in this answer :)

Why variables have to be final in anonymous methods and class fields don't

The local variable is allocated in the stack, and it will fall out of scope after testMethod(). Making the variable final ensures that it is ok to just pass a reference to it to the anonymous class. If it was not final, a later assignment to it in testMethod() could change the value later with confusing results. (The user might expect the later assigned value used, but that would be impossible).

A field of the parent class, however can be accessed through the parent reference of the anonymous class, so any later assignments can be handled without confusion.

Are all final variables captured by anonymous classes?

The language spec has very little to say about how anonymous classes should capture variables from their enclosing scope.

The only especially relevant section of the language spec that I can find is JLS Sec 8.1.3:

Any local variable, formal parameter, or exception parameter used but not declared in an inner class must either be declared final or be effectively final (§4.12.4), or a compile-time error occurs where the use is attempted.)

(Anonymous classes are inner classes)

It does not specify anything about which variables the anonymous class should capture, or how that capturing should be implemented.

I think it is reasonable to infer from this that implementations need not capture variables that aren't referenced in the inner class; but it doesn't say they can't.

Why should my local variables be final to be accessible from anonymous class?

When you access a final variable from an anonymous class, the compiler secretly copies their value into a member variable of the anonymous class. eg:

Runnable foo() {
final int x = 42;
return new Runnable() {
void run() {
System.out.writeln(x);
}
};
}

becomes:

// the actual name is generally illegal in normal java syntax
class internal_Runnable implements Runnable {
final int x;
internal_Runnable(int _x) { x = _x; }
void run() {
System.out.writeln(x);
}
}

void foo() {
final x = 42;
return new internal_Runnable(x);
}

If the variable were not final and were allowed to change, the value cached in the anonymous class instance could go out of sync. This could have been avoided by use of a closure - that is, an object holding the values of all local variables, that both the original function and the new anonymous class instance access. .NET uses closures, for example. However, this can incur a performance hit, and perhaps for that reason, the Java language designers decided not to support full closures.

Why Java inner classes require final outer instance variables?

Well first, let's all relax, and please put that gun down.

OK. Now the reason the language insists on that is that it cheats in order to provide your inner class functions access to the local variables they crave. The runtime makes a copy of the local execution context (and etc. as appropriate), and thus it insists that you make everything final so it can keep things honest.

If it didn't do that, then code that changed the value of a local variable after your object was constructed but before the inner class function runs might be confusing and weird.

This is the essence of a lot of the brouhaha around Java and "closures".


Note: the opening paragraph was a joke in reference to some all-caps text in the original composition of the OP.

final for anonymous class?

I am assuming you are using Java 8 or above. In Java 8 and above, anonymous classes can access local variables that are "effectively final", i.e. they are not explicitly declared final, but they are never assigned to, so they are eligible to be declared final. The local variable j is effectively final because it is never assigned to after initialization, so assuming this is Java 8+, you can use it in the anonymous class. Variable i is an instance variable, so it is implicitly accessed through A.this, and thus it doesn't matter if i is final or not.

Java local variable visibility in anonymous inner classes - why is 'final' keyword required?

Your SelectionAdapter is an anonymous inner class and I think this makes it clear:

Local classes can most definitely reference instance variables. The reason they cannot reference non final local variables is because the local class instance can remain in memory after the method returns. When the method returns the local variables go out of scope, so a copy of them is needed. If the variables weren’t final then the copy of the variable in the method could change, while the copy in the local class didn’t, so they’d be out of synch.

Anonymous inner classes require final variables because of the way they are implemented in Java. An anonymous inner class (AIC) uses local variables by creating a private instance field which holds a copy of the value of the local variable. The inner class isn’t actually using the local variable, but a copy. It should be fairly obvious at this point that a “Bad Thing”™ can happen if either the original value or the copied value changes; there will be some unexpected data synchronization problems. In order to prevent this kind of problem, Java requires you to mark local variables that will be used by the AIC as final (i.e., unchangeable). This guarantees that the inner class’ copies of local variables will always match the actual values.

why is anonymous class able to access the local variable

Anonymous Classes, The JavaTM Tutorials:

  • An anonymous class cannot access local variables ... that are not declared as final or effectively final.

The variable i is not changed, so it is effectively final. For this reason, its value can be used by the anonymous class.

Final variables within anonymous classes

Yes, each value of in is preserved in each of the Threads that is created. You can use a local variable in an anonymous inner class as long as it's declared final, or if you're using Java 8, if it's "effectively final" (not final but not changed).



Related Topics



Leave a reply



Submit