Mystery of Accessibility in Local Inner Classes

Here is an interesting concept which I came across just recently and considered it worth sharing. I was reading about inner classes concepts where in, there is a type of inner class called the local inner class. Local inner classes are those classes which reside within the function of a method belonging to an outer class. The code can be something shown like this.

public class LocalInnerClassTest {
	public void defineInnerClass() {
		class MyLocalInnerClass {
			public void doSomething() {				
			}
		}
 
	}
}

Now lets suppose we want to pass a variable in the defineInnerClass() and pass it to the doSomething() for some computation, then according to the specifications on local inner classes methods we must declare the variables as final or else it will result in a compile time error. So the resulting code must be something as follows:

public class LocalInnerClassTest {
	public void defineInnerClass(final int var) {
		class MyLocalInnerClass {
			public void doSomething() {
				System.out.println(var);
			}
		}
	}
}

where var is the variable that must be declared as final to be passed into the doSomething() method of MyLocalInnerClass. Now the mysterious question which I faced was why exactly such a specification has been outlined. Why can’t the inner class simply take the variable as it is and process further.

In order to understand this reason as to why local inner classes can access only final variables, we have to learn how exactly inner classes get translated to the byte code. The moment you come to know this, you can easily see the logic behind making the variables final.

How Are Inner Classes Translated to the Byte Code

Here is the secret. Inner classes as you must have known them from a long time are still mysterious to the JVM. Yes its true. Inner classes have been implemented only to the compiler level. When the classes are compiled which contain inner classes, the byte code which gets generated does not actually implement inner classes as a class within a class. The book on Core Java from makes this statement:

“Inner classes are translated into regular class files with $ (dollar signs) delimiting outer and inner class names and the virtual machine does not have any special knowledge about them”

That means when the above class file from the example is compiled it will generate two class files such as:

  1. LocalInnerClassTest.class
  2. LocalInnerClassTest$MyLocalInnerClass.class

Unfurling the mystery of final variables

If you apply logic to the above theory of inner class at the byte code level, you have the answer to the mystery of having final variables. For explanation purpose, lets take the same example.

First lets say we make a call to the defineInnerClass() by creating an instance of LocalInnerClassTest. At this point, the instance of MyLocalInnerClass is still not present because the JVM treats it as a separate class at the byte code level. So when the call to defineInnerClass() is made the JVM tries to instantiate an object of MyLocalInnerClass.

But here we run into a problem. The function doSomething() accesses the var variable which is passed down from the outer class method. If you can simply apply logic over here, you can see the problem. How should the JVM pass the variable which has been declared in one class file to the method in another class file?

In order to solve this big problem, the JVM acts smart. It makes a requirement for the developer to make the variable passed from the method of an outer class to be declared as final. How would this solve the problem, you may ask? When you declare the variable var as final the compiler does a trick. The trick being, it quietly places a hidden variable with the name val$var inside the 2nd compiled class file.

The variable val$var is assigned the same value which has been assigned to var since now the compiler knows that the value cannot be changed as it has been declared final. This is very clever, since final variable must always be assigned before compilation.

So there you go. Now, when you run the program from within an application the inner class already has the value which has been assigned to var through its inner hidden variable val$var and thus the mystery gets solved. Therefore you have the concept that local inner class methods can have access to only the final variables of the outer class.

Hey, Local Inner Classes can even access Outer Class member variables directly. How?

Ok, now that we have tackled the above mystery, this is more simpler. The below code gets perfectly compiled

public class LocalInnerClassTest {
     private int var2;
     public void defineInnerClass(final int var){
 	  class MyLocalInnerClass{
 		public void doSomething(){
 			System.out.println(var+var2);
 		}
     	  }
     }
}

You can notice that the local inner class has direct access to even the private variable var2 of its outer class. The reason behind the accessibility of outer member variables directly inside the local inner classes is that, once again the compiler cleverly places one more hidden variable named as this$0 which is a final instance variable of the outer class type. When the inner class object is instantiated the variable this$0 is given a reference to the outer variable with direct access privileges. Hence you can access the outer class member variables directly from within the inner class. Smart isn’t it?

26 thoughts on “Mystery of Accessibility in Local Inner Classes

  1. Good explanation, I suspect many don’t know about this.

    Small suggestion, renake myLocalInnerClass to MyLocalInnerClass, it reads easier when you’re used to standard Java convensions.

    Obvious next question is then, how does the various closure proposals escape this problem?

  2. @Casper – Java 7 will be supporting closures but I think it will be like adding a layer on top of inner classes to remove unnecessary code. I have a feeling that it would be somewhat similar to the introduction of annotations to remove loads of XML configurations.

    What are your thoughts?

  3. @Steve – People have slowly started to stay away from your OS. Don’t care about them though. Enjoy killing the much needed abilities and playing with your lousy toy.

  4. Nice and easy to understand explanation. But maybe you can explain me a similar thing I am currently facing:

    abstract class Inner {
    Inner() { run(); }
    public abstract void run();
    }
    public class Outer {
    public static void test(final String s) {
    Inner lInner = new Inner() {
    public void run() { System.out.println(“s = ” + s); } };
    lInner.run();
    }
    public static void main(String[] args) { test(“hello”); }
    }

    The output is:
    null
    hello

    Why is “s” null within the constructor of “Inner”?

  5. One query, not able to get it ,

    class LocalInnerClassTest$1MyLocalInnerClass extends java.lang.Object{
    final int val$var;
    final LocalInnerClassTest this$0;
    LocalInnerClassTest$1MyLocalInnerClass(LocalInnerClassTest, int);
    public void doSomething();
    }

    i got it by javap,

    Why the inner class generated with final keyword for val$var,

    cant we achieve same without final word?

  6. I might be wrong, but got answer while surfing
    why they kept it that way

    1. Method local paramters are gone at the end of the method
    2. where as the inner class might be available in heap and it needs those variable value.
    3. to make it if they mark it final, itwill be available permanently till class available and wont change

  7. @nitinpai: It seems to work property since JDK 1.5. I tested it with JDK 1.4.2, and it did not work.

    Decompilation reveals why:

    JDK 1.4.2:

    class Outer$1 {
    Outer$1(final s) { var$s = s }
    }

    JDK 1.5:
    class Outer$1{
    Outer$1(final s) { var$s = s;
    super(); }
    }

    So JDK 1.4 calls the super-constructor before setting the hidden var$s, and JDK 1.5 first sets the var$s and then calls the super-constructor (I thought this is not allowed in Java, because super() must be the first instruction in the constructor method).

  8. > I thought this is not allowed in Java, because super() must be the first instruction in the constructor method.

    Well it’s not allowed in Java, but it is allowed by the bytecode verifier – which has other ways of ensuring that the superclass constructor is always called.

  9. I think the explanation is not as self-evident as your post makes it sound; in fact, it explains only half of the problem. The compiler does not need to insist on the variable to be final to use the val$var trick – it is sufficient for it to know that the variable has been assigned to prior to the call of the inner class constructor. Compiler already knows how to track that – this is how it alerts you of the variables used before being assigned to; this rule applies to all variables.

    The second part of the puzzle has to do with semantic of the Java language. Imagine that the compiler drops the ‘final’ requirement, requiring only that the variable is initialized prior to the call of the constructor. The data can make into the inner class just OK, but since we’ve made a copy, the new variable gets a lifecycle of its own. This means that any assignments to it will not be reflected in the value of the local variable, thus exposing the fact that the compiler has performed a copy. In other words, the programmers will see two variables when in fact they have declared only one; a requirement of declaring the variable final gracefully deals with this inconsistency.

  10. great explanation … but can u tell me …. if any one ask me ” how do you know java bite code makes a copy of final variable under the inner class ? ” … what will be my answer

  11. Pl. Note:
    The instance variables of the enclosing class cannot be accessed from the local method of a static method.

Leave a Reply

Your email address will not be published. Required fields are marked *