Static nested & Inner classes

A class inside another class is called a Nested Class. In other words, as a class has member variables and member methods it can also have member classes.

Nested classes are divided into two categories: static and non-static. Nested classes that are declared static are called static nested classes. Non-static nested classes are called inner classes.

You can say inner classes are of 3 types:

  • Regular Inner classes
  • Method-local inner classes
  • Anonymous inner classes

Why Use Nested Classes?

  • It is a way of logically grouping classes that are only used in one place: If a class is useful to only one other class, then it is logical to embed it in that class and keep the two together. Nesting such “helper classesâ€? makes their package more streamlined.
  • It increases encapsulation: Consider two top-level classes, A and B, where B needs access to members of A that would otherwise be declared private. By hiding class B within class A, A’s members can be declared private and B can access them. In addition, B itself can be hidden from the outside world.
  • It can lead to more readable and maintainable code: Nesting small classes within top-level classes places the code closer to where it is used.

Inner classes

These are just “regular� inner classes which are not method-local, anonymous or static. Suppose you have an inner class like this:


class MyOuter {
    class MyInner { }
}

When you compile it:


javac MyOuter.java

you will end up with two class files:


MyOuter.class
MyOuter$MyInner.class

The inner class is still, in the end, a separate class, so a separate class file is generated for it. But the inner class file isn’t accessible to you in the usual way. You can’t say


java MyOuter$MyInner

in hopes of running the main() method of the inner class, because a regular inner class cannot have static declarations of any kind. The only way you can access the inner class is through a live instance of the outer class.

In other words, only at runtime, when there’s already an instance of the outer class to tie the inner class instance to.

Now with the basics done, let’s see an inner class performing some actions:


class MyOuter {
    private int x = 7;

    // inner class definition
    class MyInner {
        public void seeOuter() {
            System.out.println("Outer x is " + x);
        }
    } // close inner class definition
} // close outer class

The output of the above program would be Outer x is 7. This happens because an inner class can access private members of its outer class even those which are declared as static. The inner class is also a member of the outer class. So just as any member of the outer class (say, an instance method) can access any other member of the outer class, private or not, the inner class (also a member) can do the same.

Instantiate the inner class

To create an instance of an inner class, you must have an instance of the outer class to tie to the inner class. There are no exceptions to this rule: An inner class instance can never stand alone without a direct relationship to an instance of the outer class.

Instantiating an Inner Class from Within the Outer Class


class MyOuter {
    private int x = 7;

    public void makeInner() {
        MyInner in = new MyInner();  // make an inner instance
        in.seeOuter();
    }

    class MyInner {
        public void seeOuter() {
            System.out.println("Outer x is " + x);
        }
    }
}

The reason the above syntax works is because the outer class instance method code is doing the instantiating. In other words, there’s already an instance of the outer class i.e, the instance running the makeInner() method.

Instantiating an Inner Class from Outside the Outer Class Instance Code


public static void main(String[] args) {
    MyOuter mo = new MyOuter(); // gotta get an instance of outer class
    MyOuter.MyInner inner = mo.new MyInner(); // instantiate inner class from outer class instance
    inner.seeOuter();
}

As we know that we must have an outer class instance to create an inner class instance, the above code would be the way to go. Instantiating an inner class is the only scenario in which you’ll invoke new on an instance as opposed to invoking new to construct an instance.

If you are a one liner then the below code is for you:


public static void main(String[] args) {
     MyOuter.MyInner inner = new MyOuter().new MyInner(); // all in one line
     inner.seeOuter();
}

Referencing the Inner or Outer Instance from Within the Inner Class

An object refers to itself normally by using the this reference. The this reference is the way an object can pass a reference to itself to some other code as a method argument:


public void myMethod() {
    MyClass mc = new MyClass();
    mc.doStuff(this); // pass a ref to object running myMethod
}

So within an inner class code, the this reference refers to the instance of the inner class, as you’d probably expect, since this always refers to the currently executing object. But when the inner class code wants an explicit reference to the outer class instance that the inner instance is tied to, it can access the outer class this like shown below:


class MyOuter {
    private int x = 7;

    public void makeInner() {
        MyInner in = new MyInner();
        in.seeOuter();
    }

    class MyInner {
        public void seeOuter() {
            System.out.println("Outer x is " + x);
            System.out.println("Inner class ref is " + this);
            System.out.println("Outer class ref is " + MyOuter.this);
        }
    }

    public static void main(String[] args) {
        MyOuter.MyInner inner = new MyOuter().new MyInner();
        inner.seeOuter(); // works fine
        System.out.println(inner.x); // compiler error as you can't directly
                                     // access outer class member through a
                                     // inner class reference (you can have 
                                     // getter & setter in the inner class)
    }
}

Method-Local Inner Classes

A regular inner class scoped inside another class’s curly braces, but outside any method code (in other words, at the same level that an instance variable is declared) is called a method-local inner class.


class Outer {    
    private String x = "Outer";

    void doStuff() {
        
        class Inner {
            
            public void seeOuter() {
                System.out.println("Outer x is " + x);
            } // close inner class method
            
        } // close inner class definition
    }
}

In the above example, class Inner is the method-local inner class. But the inner class is useless because you are never instantiating the inner class. Just because you declared the class doesn’t mean you created an instance of it.

So to use the inner class, you must make an instance of it somewhere within the method but below the inner class definition (or the compiler won’t be able to find the inner class).

The following legal code shows how to instantiate and use a method-local inner class:


class Outer {    
    private String x = "Outer";

    void doStuff() {
        
        class Inner {
            
            public void seeOuter() {
                System.out.println("Outer x is " + x);
            } // close inner class method
            
            MyInner mi = new MyInner();  // This line must come
                                         // after the class
                                         
            mi.seeOuter();
            
        } // close inner class definition
    }
}

What a Method-Local Inner Object Can and Can’t Do

  • Access private members of the outer/enclosing class.
  • Cannot use the local variables of the method the inner class is in.
  • The only modifiers you can apply to a method-local inner class are abstract and final, but, as always, never both at the same time.
  • You can’t mark a method-local inner class public, private, protected, static or transient (just like local variable declaration).

Important Points

Method-local inner class cannot access the local variables of the method inside which it is defined unless the variables are marked final. This is because the local variables of the method live on the stack and exist only for the lifetime of the method.

The scope of a local variable is limited to the method the variable is declared in. When the method ends, the stack frame is blown away and the variable is history.

But even after the method completes, the inner class object created within it might still be alive on the heap if, for example, a reference to it was passed into some other code and then stored in an instance variable. Because the local variables aren’t guaranteed to be alive as long as the method-local inner class object is, the inner class object can’t use them. Unless the local variables are marked final.


class MyOuter {
    private String x = "Outer";

    void doStuff() {
        String z = "local variable";
        
        class MyInner {
            public void seeOuter() {
                System.out.println("Outer x is " + x);