Java Anonymous Inner Classes and Effectively Final variables
Whenever we code an anonymous inner class in java, we might have come across the following compile time error.
What is this error and why does it want the local variable to be effectively final?
Anonymous Inner Class
According to Oracle Java docs, anonymous classes are defined as
Anonymous classes enable you to make your code more concise. They enable you to declare and instantiate a class at the same time. They are like local classes except that they do not have a name. Use them if you need to use a local class only once.
Anonymous inner classes makes the code more readable and reduces the number of classes. Anonymous inner classes can be used in a variety of places where we need to have custom implementation of an interface and instantiate that implementation only once.
Anonymous Inner Classes and Final variables
In the code snippet shown below, I have created a Integer list and to iterate over the list, instead of using the default iterator provided by the ArrayList class, I have created my own custom iterator where we will iterator over only even numbers in that list.
This compiles and runs successfully as long as the intergerList local variable in the main method is either a final variable or assigned value only once. In our case, we have assigned a new ArrayList object only once which makes it effectively final (Effectively final means variables that are assigned value only once and not declared as final. The compiler intelligently infers them as final variable which makes sense).
If we assign value more than once then compiler throws the error shown in the screenshot below
But why not access local variables that are not final or effectively final?
While compiling, Java compiler creates two class files, one class file for the “AnonymousTest” class containing the main method show above and another one for the inner anonymous iterator class we have created and instantiated inside the main method.
Using an online decompiler I decompiled both the class files to see how anonymous inner classes are generated by the compiler and how they are used.
The below screenshot shows the decompiled class file of anonymous iterator we created.
Inside the anonymous iterator class we use the integer list(a local variable) we created in the main method and that variable gets created as final member variable in the generated class file.
In the main class also the integer list variable is created as final like shown below.
The variable is final because of the following constraints. when the anonymous iterator object is constructed, the integerList object is also copied from main method to the anonymous iterator object. So now the main method and the anonymous iterator object both are operating on the same value of the integerlist object.
Assume that integerlist variable is not final. And assume that after we create the anonymous iterator object, we assign new ArrayList object to the integerlist variable in main method. Now the anonymous iterator object has a out of date value of the integerlist variable. This will lead to all sorts of runtime bugs.
The reverse could also happen. That is , anonymous iterator object could change the value of the integerlist variable and the main method might not know about it.
So making the variable final, solves all these issues. But this is just a simple explanation of a broader concept called Closures. Each programming language has its own way of handling these type of situations involving Closures. We have explored how it is handled here in java.
If you are curious and want to explore more about closures, check out these links.