Tuesday, July 18, 2017

Local Classes & 'final' local variables

Local Classes

Local classes in Java are like inner classes (non-static nested classes) that are defined inside a method or scope block ({ ... }) inside a method. Here is an example:
class Outer {

    public void printText() {

        class Local {

        }

        Local local = new Local();
    }

}
Local classes can only be accessed from inside the method or scope block in which they are defined.
Local classes can access members (fields and methods) of its enclosing class just like regular inner classes.
Local classes can also access local variables inside the same method or scope block, provided these variables are declared final.
From Java 8 local classes can also access local variables and parameters of the method the local class is declared in. The parameter will have to be declared final or be effectually final. Effectually final means that the variable is never changed after it is initialized. Method parameters are often effectually final.
Local classes can also be declared inside static methods. In that case the local class only has access to the static parts of the enclosing class. Local classes cannot contain all kinds of static declarations (constants are allowed - variables declared static final), because local classes are non-static in nature - even if declared inside a static method.
The same shadowing rules apply for local classes as for inner classes.

An anonymous class can access members of the enclosing class. It can also access local variables which are declared final or effectively final (since Java 8).

@reference_1_tutorials.jenkov.com
Java Nested Classes

The methods in an anonymous class don't really have access to local variables and method parameters. Rather, when an object of the anonymous class is instantiated, copies of the final local variables and method parameters referred to by the object's methods are stored as instance variables in the object. The methods in the object of the anonymous class really access those hidden instance variables. [1]
Thus, the local variables and method parameters accessed by the methods of the local class must be declared final to prevent their values from changing after the object is instantiated.

@reference_2_stackoverflow
Why Java inner classes require “final” outer instance variables?
@reference_3_developer.com
The Essence of OOP using Java, Anonymous Classes


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 :)

@reference_4_stackoverflow
Why are only final variables accessible in anonymous class?


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.

@reference_5_stackoverflow
Why a non-final “local” variable cannot be used inside an inner class, and instead a non-final field of the enclosing class can?

No comments:

Post a Comment