Inner Classes

An inner class is a class declared inside another class. The enclosing class can be a top level class or another inner class. The reason for using inner classes is to properly implement composition when the life of inner class instances are controlled by the outer class instance. Since the inner class instances are always linked to a specific outer class instance, usually the outer class instance manages the inner class instances and controls their life cycle.

Inner classes facts:

  • can accept any accessibility modifiers
  • they can be instantiated only in the context of an instance of the parent class
  • can access all members of the enclosing class
  • if a member in the inner class masks a member in the outer class (name collision) the inner class still has access to the outer class members: OuterClass.this.member
  • if the inner class is public it can be instantiated from outside the outer class only using an instance of the outer class: outerInstance.new Inner()
  • inner classes can be abstract or final
  • hierarchies of inner classes can be created inside the outer class or inside classes that extend the outer class

When inner classes are kept accessible only to the outer classes and their derived classes the syntax is pretty clear and the benefits for the design are clarity and better encapsulation. On the other hand, when inner classes are public, they can be used in surprising ways and the syntax starts to become less comprehensible. While there are creative ways to use public inner classes to solve real problems, the code for sure will start to lack in clarity and maintainability.

The following examples exemplify the syntax and provide some food for thought.

Example 1: Basic syntax

package com.littletutorials.nested;

public class Outer
{
    private int value;

    public Outer(int value)
    {
        this.value = value;
    }

    public class Inner1
    {
        private int value = 1000; // hides the outer member

        public void f1()
        {
            // access outer members when hidden
            System.out.println(getClass().getName() + ": " + ++Outer.this.value);
        }
    }

    private class Inner2
    {
        public void f2()
        {
            // access outer members directly
            System.out.println(getClass().getName() + ": " + ++value);
        }
    }

    public void f()
    {
        System.out.println(getClass().getName() + ": " + ++value);
        Inner2 i2 = new Inner2();
        i2.f2();
    }
}

package com.littletutorials.nested;

public class Test1
{
    public static void main(String[] args)
    {
        Outer o = new Outer(5);
        o.f();

        // create an instance of the inner class using an outer class instance
        Outer.Inner1 i1 = o.new Inner1();
        i1.f1();
    }
}

Example 2: Extending a public inner class as a top level class

package com.littletutorials.nested;

public class O1
{
    public class I1
    {
        public O1 getOuter()
        {
            return O1.this;
        }
    }
}

package com.littletutorials.nested;

public class O1I1 extends O1.I1
{
    public O1I1(O1 o) // we need an instance of the outer class
    {
        o.super();
    }
}

package com.littletutorials.nested;

public class Test2
{
    public static void main(String[] args)
    {
        // outer instance
        O1 o1 = new O1();

        // create instance of the class derived from inner
        O1I1 o1i1 = new O1I1(o1);

        // create an inner instance
        O1.I1 i1 = o1.new I1();
        System.out.println("enclosing " + i1.getClass() +
            " is " + i1.getClass().getEnclosingClass());

        O1 o11 = o1i1.getOuter();

        if (o1 == o11)
        {
            System.out.println("Bingo! But...");
            System.out.println("enclosing " + o1i1.getClass() +
                " is " + o1i1.getClass().getEnclosingClass());
        }
        else
        {
            System.out.println("??");
        }
    }
}

Example 3: Extending an inner class inside an extended outer class

package com.littletutorials.nested;

public class O2
{
    private int value;

    class I2
    {
        public int inc()
        {
            return ++value;
        }
    }
}

package com.littletutorials.nested;

public class O3 extends O2
{
    class I3 extends I2
    {
        public int verboseInc()
        {
            // return ++value;  // wrong value is not visible
            return inc();
        }
    }
}

Each problem is different and requires a different solution. Java provides considerable flexibility in using inner classes but some of the capabilities bring considerable complexity. The best approach a designer can take is to use the capabilities of a language according to their design and scope intent and to avoid stretching the sometimes difficult to control side effects.

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

2 thoughts on “Inner Classes”

  1. It sounds fantastic. The question is – this post is absolutely new and original, isn’t it? It seems to me I’ve saw it somewhere before.

  2. It is as original as possible, I didn’t invent inner classes :). On the other hand I wrote the post and the examples myself, they are not copied. It is meant to be more a reminder of the syntax.

Comments are closed.