Local Inner Classes

Local inner classes are declared inside of a block of code. This block can be static bloc, a constructor, a method or simply a block of code surrounded with curly braces. These classes are only visible inside the enclosing bloc, but inside the block full hierarchies of classes can be developed. Local inner classes can implement any interface or extend any class as long as those are visible and extendable. On the other hand interfaces cannot be declared in a local scope.
Local inner classes can access variables declared in the enclosing block as long as they are final. When the enclosing block is a constructor or a method the final parameters of the constructor or method are also accessible, of course for reading.

Members of the enclosing class are also accessible regardless of their access modifier. Keep in mind that a class declared in a static block can only access static members of the enclosing class.

Another interesting limitation is the fact that local inner classes cannot declare static blocks. Also a local inner cannot have the same name (hide) as an enclosing class.

The next code sample shows how the syntax for local inner classes looks like.

package com.littletutorials.nested;

public class Outer
{
    private static String s = "outer member";
    private int a = 1;

    static
    {
        final int b = 2;
        int c = 3;

        // local inner class in a static block
        final class LocalInner
        {
            public void f()
            {
                System.out.print(getClass().getName() + " inner in ");
                System.out.println(getClass().getEnclosingClass());
                System.out.println(s);
                System.out.println("b = " + b);
                // a = 2; // error: a is not static
                // c = 4; // error: a is not final
            }
        }

        LocalInner l = new LocalInner();
        l.f();
    }

    public Outer(final String p1, String p2)
    {
        int a = 4; // hide the class member
        final int b = 5;

        // local inner class in a constructor
        class LocalInner
        {
            final String s2 = "inner member";
            public void f()
            {
                // declare a local inner in a method of a local inner
                class InnerInner
                {
                    public void f()
                    {
                        System.out.print(getClass().getName() + " inner in ");
                        System.out.println(getClass().getEnclosingClass());
                        System.out.println(Outer.this.s);
                        System.out.println(LocalInner.this.s2);
                    }
                }

                System.out.print(getClass().getName() + " inner in ");
                System.out.println(getClass().getEnclosingClass());
                // direct access to members of the top level class
                System.out.println(s);
                // qualified access to hidden members of the top level class
                Outer.this.a = 2;
                // access to variables declared in the enclosing block
                System.out.println(p1);
                // System.out.println(s2); // error: p2 is not final
                // a = 5; // error: a is not final
                System.out.println("b = " + b);

                // use the local inner
                InnerInner i = new InnerInner();
                i.f();
            }
        }

        // use the local inner
        LocalInner l = new LocalInner();
        l.f();
    }

    public static void main(String[] args)
    {
        Outer o = new Outer("final param", "non-final param");
    }
}

When executed the main method of the Outer class declared above will produce this output that reveals the naming conventions used by the compiler for local inner classes:

com.littletutorials.nested.Outer$1LocalInner inner in class com.littletutorials.nested.Outer
outer member
b = 2
com.littletutorials.nested.Outer$2LocalInner inner in class com.littletutorials.nested.Outer
outer member
final param
b = 5
com.littletutorials.nested.Outer$2LocalInner$1InnerInner inner in class com.littletutorials.nested.Outer$2LocalInner
outer member
inner member

Local inner classes can be very useful in declaring classes with a very limited scope and with short implementations. A perfect example are implementations of listener interfaces or extensions of abstract adapters. Other useful scenarios can be imagined, for example the initialization of a map of action classes with unique and short implementations in a static block. These constructs offer the convenience of not creating a new file for a small class and can also help with encapsulation. On the other hand when they are over used they can make the code hard to read and debug and they can become a nightmare for maintainers.

This post is part of a series explaining the Java concept of defining classes in other classes: