Skip to main content

Using inner and nested Java classes

Posted by hellofadude on February 20, 2014 at 7:12 PM PST

If you have followed previous posts, you might begin to perceive a pattern in the semantics of the Java programming language. If not, it might help to go over previous posts as I tend to return to expand on previous topics or add clearer examples as time permits. Inner classes might at first seem like a whole new language to the uninitiated but they are a nice feature in Java that allow you to logically group related classes and control the visibility of one class from outside of the other. Even more than that, inner classes provide you with the ability to inherit from more than one interface or implementation in a kind of "multiple implementation inheritance". The code in an inner class can be used to manipulate the members of the outer class within which it exists. Inner classes are not limited to the functionality of the base class of the outer class, but can inherit independently of it.

Whereas outer classes allow you to undertake some kind of multiple inheritance with interfaces, inner classes allow you to do the same thing not only with interfaces but with abstract or concrete classes. This mechanism allows you to code solutions to some otherwise intractable programming problems. The syntax for creating inner classes is pretty straightforward:-

package kingsley.java;

public class Company {
     class Employee {
         private String name;
         Employee(String name) {
             this.name = name;
         }
         public String getName() {
             return name;
         }
     }
     class Department {
         private String name;

         Department (String name) {
             this.name = name;
         }
         public String getName() {
             return name;
         }
     }
     public Employee employed(String name) {
         return new Employee(name);
     }
     public Department department(String name) {
         return new Department(name);
     }
     public void newStarter(String name, String department) {
         Employee emp = employed(name);
         Department dpt = department(department);
         System.out.println(emp.getName() + " is a member of " +
dpt.getName());
     }
     public static void main(String[] args) {
         Company c = new Company();
         c.newStarter("kingsley", "IT");
         // Referencing inner classes
         Company.Employee ce = c.employed();
         Company.Department cd = c.department();
    }
}
/* Output
kingsley is a member of IT
*//

Here, the outer class Company has methods that return references to the inner classes Employee and Department. The newStarter() within the outer class is able to use inner classes like it would any other class, with the only difference being these classes are actually nested within Company. In the main() notice how we reference objects of the inner classes with OuterClassName.InnerClassName. This is necessary because objects of any non-static inner class can only be created in association with an object of the outer class, outside of any non-static method within the outer class.

As a consequence, objects of the inner classes you create usually have access to all the private and protected members of the surrounding class without any special syntax. This is because when you create an inner class in Java, the compiler captures a reference to the particular enclosing class within which the inner class exists, thus making it possible to reference members of the enclosing class, in this way, a derived inner class can be used to manipulate the members of an enclosing class. The following example demonstrates this clearly:-

package kingsley.osime;

interface Repeater {
     boolean start();
     String current();
     void next();
}
public class StringArray {
     private static String[] sequence;
     public StringArray(String s) {
         this.sequence = s.split(" ");
     }
     private class ReverseSequence implements Repeater {
         private int i = 0;
         public boolean start() {
             return i == sequence.length;
         }
         public String current() {
             return sequence[sequence.length-(i+1)];
         }
         public void next() {
             if(i < sequence.length)
                 ++i;
         }
      }
     public Repeater repeater() {
         return new ReverseSequence();
     }
     public static void main(String[] args) {
         StringArray sa = new StringArray("Good sense is of all things
the most " +  "equally distributed among men");
         Repeater repeater = sa.repeater();
         while(!repeater.start()) {
             System.out.print(repeater.current() + " ");
             repeater.next();
         }
     }
}
/* Output
men among distributed equally most the things all of is sense Good
*//

In this example, the derived inner class RequestSequence is used to manipulate the array member within the enclosing class by providing functionality for an interface that acts like an iterator. In this instance the content of the array member is read in reverse.

Multiple implementation inheritance

With inner classes you can solve the problem of multiple implementation inheritance when you are limited to working with abstract or concrete classes as opposed to interfaces:-

package Innerclasses.java;

abstract class A {}
class B {}

class C extends A {
     class D extends B {
     }
}
public class MultipleImplementation {
     static void takesA(A a) {}
     static void takesB(B a) {}
      public static void main(String[] args) {
          C c = new C();
          takesA(c);
         // using dot new to create object
         //inner class directly
          takesB(c.new D());
     }
}

Note the use of an object of the outer class followed by the new keyword to create an object of the inner class in the takesB() method. Java will not allow you to create an object of the inner class directly, unless you already have an object of the outer class. The only exception to this rule is when you are dealing with nested classes i.e. inner classes that have been declared static

.this keyword

If you want to reference the outer class from within the inner class, you must first reference the outer class followed by a dot and then the this keyword like so:-

package kingsley.osime;

public class Outer {
     void d() { System.out.println("Outer d()"); }
     public class Inner {
         public Outer getOuter() {
             return Outer.this;
         }
     }
     public Inner getInner() { return new Inner(); }
     public static void main(String[] args) {
         Outer ot = new Outer();
         Outer.Inner oi = ot.getInner();
         oi.getOuter().d();
     }
}
/* Output
Outer d()
*//

If you were to use the this keyword on its own from within the inner class, Java will return a reference to the inner class itself. Just as an inner class is able to reference the members of the outer class without qualification, an outer class is likewise able to access the private elements of the inner class but must do so by directly referencing the inner class:-

package Innerclasses.java;

public class Outer {
     class Inner {
         private String s = "My String";
     }
     public String innerString() {
         return new Inner().s;
     }
     public static void main(String[] args) {
         Outer ot = new Outer();
         System.out.println(ot.innerString());
         Outer.Inner oi = ot.new Inner();
     }
}
/* Output
My String
*//

Upcasting inner classes

Inner classes provide a very useful way to hide implementation when inheriting from a base class or interface, in this way, one may restrict the visibility of one's classes from client programmers by providing only a reference to the base class or interface. Here is a modified version of an earlier example to illustrate this point; we modify our Employee and Department implementations to interfaces and inherit from both using private and protected inner classes here:-

package Innerclasses.java;

import Interfaces.java.Department;
import Interfaces.java.Employee;

  class Company1 {
     String employee;
     String department;
     protected class NEmployee implements Employee {
         public String getName() {
             return employee;
         }
     }
     private class NDepartment implements Department {
         public String name() {
             return department;
         }
     }
     public Employee employed() {
        return new NEmployee();
     }
     public Department getName() {
        return new NDepartment();
     }
}
public class NewCompany {
     public static void main(String[] args) {
         Company1 c1 = new Company1();
         Employee emp = c1.employed();
         Department dept = c1.getName();
         Company1.NEmployee ce = c1.new NEmployee();
         // Can not access private inner class
         //!  Company1.NDepartment cn = c1.new NDepartment();
     }
}

Note that when you apply the private access specifier to a derived inner class you restrict access to that class to the client programmer thereby preventing any type coding dependencies. Take note of the upcasting taking place within the public interface of the outer class from a more specific class to the more generalised type. Further, unlike a private inner class, a protected inner class does allow some limited access.

Inheriting from inner classes

On the subject of inheritance, Java does not prevent you from inheriting from an inner class when such a design make sense to you. The only requirement is that you make the association between the outer class and inherited inner class, explicit like so:-

class Outer {
     class Inner {}
}
public class InnerInheritance extends Outer.Inner {
     InnerInheritance(Outer ot) {
         ot.super();
     }
     public static void main(String[] args) {
         Outer ot = new Outer();
         InnerInheritance ii = new InnerInheritance(ot);
     }
}

When you inherit from an inner class, you need to add special syntax to the constructor as the default constructor will not work. Note how we reference the enclosing class of the inherited inner class from within the derived class constructor in order to provide the correct reference.

Local and anonymous inner classes

Java also allows you to create what we refer to as local and anonymous inner classes that exist within method scopes. A local inner class is a class created within the scope of a method as opposed to just being within a class. You use a local inner class to return a reference when you're implementing an interface and to help solve some very complex programming problems whereby a class might be the only means of sufficiently expressing the level of complexity required.

An anonymous inner class is a local inner class that has no name and can be expressed within a method or arbitrary scope. The syntax for both types of classes is like so:-

package Innerclasses.java;

import Interfaces.java.Employee;

public class Company2 {
     public Employee createEmployee(String name) {
         class NewEmployee implements Employee {
             String name;
             public NewEmployee(String myName) {
                 this.name = myName;
             }
             public String getName() {
                 System.out.println(name);
                 return name;
             }
         }
         return new NewEmployee(name);
     }
      public Employee createEmployee2() {
         return new Employee() {
             String name = "kingsley-Anonymous";
             public String getName() {
                 System.out.println(name);
                 return name;
             }
         };
     }
     public static void main(String[] args) {
         Company2 c2 = new Company2();
         Employee em = c2.createEmployee("Kingsley-local");
         Employee em2 = c2.createEmployee2();
         em.getName();
         em2.getName();
     }
}
/* Output
Kingsley-local
kingsley-Anonymous
*//

As you can see, the local inner class begins and ends within the scope of a method, aside from which it is no different to how you would implement any other class.
The anonymous inner class provides exactly the same functionality as an inner class except that it is shorthand and quicker to write because you write the class as an expression within the method or scope. Here we simply implement the methods within the interface as we would do if we were implementing in a class. Notice the semi-column at the end of the expression as if it were any other statement in Java. Theoretically, we can implement local and anonymous inner classes within any arbitrary scope surrounded by curly braces.

Nested classes

Nested classes are inner classes that have been declared static. When you declare an inner class static, you do not require an object of the outer class to create the inner class and you are restricted from accessing a non-static outer class from an object of a nested class. Here is our Company example modified to use nested inner classes:-

package Innerclasses.java;

import Interfaces.java.Department;
import Interfaces.java.Employee;

public class Company4 {
     private static class NewEmployee implements Employee {
         private String name;
         NewEmployee(String name) {
             this.name = name;
         }
         public String getName() {
             return name;
         }
     }
     protected static class EmployeeDepartment implements  Department {
         private String name;

         EmployeeDepartment (String name) {
             this.name = name;
         }
         public String name() {
             return name;
         }
         static class BusinessUnit {
             static String bu = " Finance ";
              static String getBU() {
                  return bu;
              }
         }
     }
     public static Employee employed(String name) {
         return new NewEmployee(name);
     }
     public static Department department(String name) {
         return new EmployeeDepartment(name);
     }
     public static void main(String[] args) {
         Employee em = employed("kingsley");
         Department dept = department("IT");
  System.out.println(Company4.EmployeeDepartment.BusinessUnit.getBU());
     }
}
/* Output
Finance
*//

In this example, note how we do not require an object of the outer class to create an object of the inner class in the main(). Unlike a non-static inner class, a nested inner class can have static data, fields and other nested static classes as you can observe from the nested BusinessUnit class.

Nesting within interfaces

Java also allows you to nest classes within interfaces when you wish to create some common code to be used by different implementations of the interface. When you put a class in an interface it is automatically public, however you must explicitly declare the class static to ensure it does not violate the normal interface rules. Here's an example that implements the enclosing interface in the nested class:-

package Innerclasses.java;

public interface NestedInterface {
     void f();
     static class InnerClass implements NestedInterface {
         public void f() {
             System.out.println("Nested Class in Interface");
         }
         public static void main(String[] args) {
             new InnerClass().f();
         }
     }
}
/*
Nested Class in Interface
*//

Implementing callbacks

At once inner classes might seem like a bit tedious and rather difficult to manage to the impatient mind, but it is worth taking the extra effort to understand some of the benefits of using inner classes in your program code.
As mentioned earlier, inner classes allow you to inherit from more than one abstract or concrete class, something you are severely restricted from doing with outer classes. Further more, a single outer class can have multiple inner classes implementing the same interface in different ways. This is a particularly powerful programming technique that allows you to implement a control type framework like the Template method design pattern.

Because inner classes retain scope information, they also allow you to provide a kind of call-back mechanism which acts like a pointer in your code. With a callback some other object is has the necessary information to reach back into the originating object at some later point in time:-

import java.util.ArrayList;
import java.util.List;

interface U {
     void a1();
     void a2();
     void a3();
}
class A {
     int i = 0;
     int count;
     public A(int i) {
         this.i = i;
         count = i++;
     }
     public U getU() {
         return new U() {
             public void a1() { System.out.println("a1() " + count); }
             public void a2() { System.out.println("a2() " + count); }
             public void a3() { System.out.println("a3() " + count); }
         };
     }
}
     class B {
         U[] ul = new U[3];
         int i = 0;
         public void addRef(U ui, int i) {
             ul[i] = ui;

         }
         public void setRef(int i) {
             System.out.println("Setting element at " + i + " to null");
             ul[i]  = null;
         }
         public void goOver(int i) {
             ul[i].a1();
             ul[i].a2();
             ul[i].a3();
         }
     }
public class Callback {
     public static void main(String[] args) {
         B b = new B();
         for(int i = 0; i < 3; i++) {
             A a1 = new A(i);
             b.addRef(a1.getU(), i);
             b.goOver(i);
          }
         b.setRef(1);
    }
}
/* Output
a1() 0
a2() 0
a3() 0
a1() 1
a2() 1
a3() 1
a1() 2
a2() 2
a3() 2
Setting element at 1 to null
*//

This simple example creates a method in class A that returns a reference to interface U by building an anonymous inner class. In a second class B we create an array containing U references and implement various methods that allows us to accept and add U references to the array, remove U references from the array and a third method through which we can move through the array and call the methods in U. In the main(), we populate a single B object with U references returned by a number of A objects. We then use B to call back into all the A objects. This is a simplification of a very powerful programming technique.

Understanding when and how to use inner classes is more of a design issue that should become second nature with repeated practice to the dedicated programmer.

Comments

The invocation of the method addRef() on the object ...

The invocation of the method addRef() on the object b::B
will generate a compile time error since it requires addRef(U, int). Other than that this topic was very well explained.

great spot!..;-)

great spot!..;-)

As an amateur programmer as myself, somehow when reviewing ...

As an amateur programmer as myself, somehow when reviewing code simple errors tend to slip through the cracks. Having said that I've missed some errors on the first pass.

Review pass #2 ->

1. On a second pass through I found errors in the invocation of the employed(String) and the department(String) methods on the c::Company object in the main method.

Should have been as follows:
Company.Employee ce = c.employed("Some argument");
Company.Department cd = c.department("Some argument");

Found:
Company.Employee ce = c.employed();
Company.Department cd = c.department();

2. When you define an inner nested class (or interface) inside an interface, the nested member is implicitly declared public and static and does not have to be explicitly declared as such.