Main Page

Previous Next

Nested Classes

All the classes we have defined so far have been separate from each other – each stored away in its own file. Not all classes have to be defined like this. You can put the definition of one class inside the definition of another class. The inside class is called a nested class. A nested class can itself have another class nested inside it, if need be.

When you define a nested class, it is a member of the enclosing class in much the same way as other members. A nested class can have an access attribute just like other class members, and the accessibility from outside the enclosing class is determined by the attributes in the same way.

public class Outside {

  // Nested class
  public class Inside {
    // Details of Inside class...
  }

  // More members of Outside class...
}

Here the class Inside is nested inside the class Outside. The Inside class is declared as a public member of Outside, so it is accessible from outside Outside. Obviously a nested class should have some specific association with the enclosing class. Arbitrarily nesting one class inside another would not be sensible. The enclosing class here is referred to as a top-level class. A top-level class is a class that contains a nested class but is not itself a nested class.

Our nested class here only has meaning in the context of an object of type Outside. This is because Inside is not declared as a static member of the class Outside. Until an object of type Outside has been created, you can't create any Inside objects. However, when you declare an object of a class containing a nested class, no objects of the nested class are necessarily created – unless of course the enclosing class's constructor creates them. For example, suppose we create an object with the following statement:

Outside outer = new Outside();

No objects of the nested class, Inside, are created. If you now wish to create an object of the type of the nested class you must refer to the nested class type using the name of the enclosing class as a qualifier. For instance, having declared an object of type Outside, we can create an object of type Inside as follows:

Outside.Inside inner = outer.new Inside();    // Define a nested class object

Here we have created an object of the nested class type that is associated with the object, outer, created earlier. We are creating an object of type Inside in the context of the object outer. Within non-static methods that are members of Outside, you can use the class name Inside without any qualification as it will be automatically qualified by the compiler with the this variable. So we could create a new Inside object from within the method of the object Outside:

Inside inner = new Inside();    // Define a nested class object

which is equivalent to:

this.Inside inner = this.new Inside();    // Define a nested class object

All this implies that a static method cannot create objects of a non-static nested class type. Because the Inside class is not a static member of the Outside class, such a member could refer to an object which does not exist – an error, if there are no Inside objects extant in the context of an Outside object. And as Inside is not a static data member of the Outside class, if a static method in the Outside class tried to create an object of type Inside directly, without first invoking an object of type Outside, it would be trying to create an object outside of that object's legitimate scope – an illegal maneuver.

Further, because the class Inside is not a static member of the Outside class, it cannot in turn contain any static data members itself. Since Inside is not static, it cannot act as a freestanding class with static members – this would be a logical contradiction.

Nested classes are typically used to define objects that at least have a strong association with objects of the enclosing class type, and often there is a tight coupling between the two. A further use for nested classes is for grouping a set of related classes under the umbrella of an enclosing class. We will be using this approach in examples later on in the book.

Static Nested Classes

To make objects of a nested class type independent of objects of the enclosing class, you can declare the nested class as static. For example,

public class Outside {
  public static class Skinside {
    // Details of Skinside
  }

  // Nested class
  public class Inside {
    // Details of Inside class...
  }
  // More members of Outside class...
}

Now with Skinside inside Outside declared as static, we can declare objects of this nested class, independent from objects of Outside, and regardless of whether we have created any Outside objects or not. For example:

Outside.Skinside example = new Outside.Skinside();

This is significantly different from what we needed to do for a non-static nested class. Now we must use the nested class name qualified by the enclosing class name as the type for creating the object. Note that a static nested class can have static members, whereas a non-static nested class cannot.

Click To expand

Let's see how a nested class works in practice with a simple example. We will create a class MagicHat that will define an object containing a variable number of rabbits. We will put the definition for the class Rabbit inside the definition of the class MagicHat. The basic structure of MagicHat.java will be:

public class MagicHat {
  // Definition of the MagicHat class...

  // Nested class to define a rabbit
  static class Rabbit {
    // Definition of the Rabbit class...
  }
}

Here the nested class is defined as static because we want to be able to have static members of this class. We will see a little later how it might work with a non-static nested class.

Try It Out – Rabbits Out of Hats

Let's add the detail of the MagicHat class definition:

import java.util.Random;                      // Import Random class

public class MagicHat {
  static int maxRabbits = 5;                  // Maximum rabbits in a hat
  static Random select = new Random();        // Random number generator

  // Constructor for a hat
  public MagicHat(String hatName) {
    this.hatName = hatName;                   // Store the hat name
    rabbits = new Rabbit[1+select.nextInt(maxRabbits)]; // Random rabbits

    for(int i = 0; i < rabbits.length; i++)
      rabbits[i] = new Rabbit();              // Create the rabbits
  }

  // String representation of a hat
  public String toString() {
    // Hat name first...
    String hatString = "\n" + hatName + " contains:\n";

    for(int i = 0; i < rabbits.length; i++)
      hatString += "\t" + rabbits[i] + " ";   // Add the rabbits strings
    return hatString;
  }

  private String hatName;                     // Name of the hat
  private Rabbit rabbits[];                   // Rabbits in the hat

  // Nested class to define a rabbit
  static class Rabbit {
    // Definition of the Rabbit class...
  }
}

Instead of the old Math.random() method that we have been using up to now to generate pseudo-random values, we are using an object of the class Random that is defined in the package java.util. An object of type Random has a variety of methods to generate pseudo-random values of different types, and with different ranges. The method nextInt() that we are using here returns an integer that is zero or greater, but less than the integer value you pass as an argument. Thus if you pass the length of an array to it, it will generate a random index value that will always be legal for the array size.

We can now add the definition of the Rabbit class. When we create a Rabbit object, we want it to have a unique name so we can distinguish one Rabbit from another. We can generate unique names by selecting one from a limited set of fixed names, and then appending an integer that is different each time the base name is used. Here's what we need to add for the Rabbit class definition:

public class MagicHat {

  // Definition of the MagicHat class – as before...

  // Nested class to define a rabbit
  static class Rabbit {
    // A name is a rabbit name from rabbitNames followed by an integer
    static private String[] rabbitNames = {"Floppsy", "Moppsy",
                                           "Gnasher", "Thumper"};
    static private int[] rabbitNamesCount = new int[rabbitNames.length];
    private String name;                             // Name of the rabbit

    // Constructor for a rabbit
    public Rabbit() {
      int index = select.nextInt(rabbitNames.length);  // Get random name index
      name = rabbitNames[index] + (++rabbitNamesCount[index]);
    }

    // String representation of a rabbit
    public String toString() {
      return name;
    }
  }
}

Note that the constructor in the Rabbit class can access the select member of the enclosing class MagicHat, without qualification. This is only possible with static members of the enclosing class – you can't refer to non-static members of the enclosing class here because there is no object of type MagicHat associated with it.

We can use the following application class to try out our nested class:

public class TryNestedClass {
  static public void main(String[] args) {
    // Create three magic hats and output them
    System.out.println(new MagicHat("Gray Topper"));
    System.out.println(new MagicHat("Black Topper"));
    System.out.println(new MagicHat("Baseball Cap"));
  }
}

When I ran the program, I got the output:

Gray Topper contains:
    Moppsy1     Moppsy2     Floppsy1 

Black Topper contains:
    Thumper1     Moppsy3     Thumper2       Gnasher1 

Baseball Cap contains:
    Moppsy4     Moppsy5     Thumper3

You are likely to get something different.

How It Works

Each MagicHat object will contain a random number of Rabbit objects. The constructor for a MagicHat object stores the name of the hat in its private member hatName, and generates a Rabbit array with at least one, and up to maxRabbits elements. This is done with the expression 1+select.nextInt(maxRabbits). Calling nextInt() with the argument maxRabbits will return a value that is from 0 to maxRabbits-1 inclusive. Adding 1 to this will result in a value from 1 to maxRabbits inclusive. The array so created is then filled with Rabbit objects.

The MagicHat class also has a method toString() method which returns a String object containing the name of the hat and the names of all the rabbits in the hat. This assumes that the Rabbit class also has a toString() method defined. We will be able to use the toString() implicitly in an output statement when we come to create and display MagicHat class objects.

The base names that we use to generate rabbit names are defined in the static array rabbitNames[] in the Rabbit class. The count for each base name, which we will append to the base name to produce a unique name for a rabbit, is stored in the static array rabbitNamesCount[]. This has the same number of elements as the rabbitNames array, and each element stores a value to be appended to the corresponding name in the rabbitNames array. The Rabbit class has the data member, name, to store a name that is initialized in the constructor. A random base name is selected from the rabbitNames[] array using an index value from 0 up to one less than the length of this array. We then append the current count for the name incremented by 1, so successive uses of any base name such as Gnasher, for example, will produce names Gnasher1, Gnasher2, and so on. The toString() method for the class returns the name for the Rabbit object.

The method main() in TryNestedClass creates three MagicHat objects and outputs the string representation of each of them. Putting the object as an argument to the println() method will call the toString()method for the object automatically, and the String object that is returned will be output to the screen.

Using a Non-Static Nested Class

In our previous example, we could define the Rabbit class as non-static by deleting the keyword static. However, if you try that, the program will no longer compile and run. The problem is the static data members' rabbitNames and rabbitNamesCount in the Rabbit class. We saw earlier that a non-static nested class cannot have static members, so we must seek an alternative way of dealing with names.

We could consider making these arrays non-static. This has several disadvantages. First, each Rabbit object would have its own copy of these arrays – an unnecessary duplication of data. A more serious problem is that our naming process would not work. Because each object has its own copy of the rabbitNamesCount array, the names generated are not going to be unique.

The answer is to keep rabbitNames and rabbitNamesCount as static, but put them in the MagicHat class instead. Let's see that working.

Try It Out – Accessing the Top Level Class Members

We need to modify the class definition to:

public class MagicHat {
  static int maxRabbits = 5;                  // Maximuum rabbits in a hat
  static Random select = new Random();        // Random number generator
  static private String[] rabbitNames = {"Floppsy", "Moppsy",
                                         "Gnasher", "Thumper"};
  static private int[] rabbitNamesCount = new int[rabbitNames.length];

  // Constructor for a hat
  public MagicHat(final String hatName) {
    this.hatName = hatName;                         // Store the hat name
    rabbits = new Rabbit[1+select.nextInt(maxRabbits)]; // Random rabbits

    for(int i = 0; i < rabbits.length; i++)
      rabbits[i] = new Rabbit();                    // Create the rabbits
  }

  // String representation of a hat
  public String toString() {
    // Hat name first...
    String hatString = "\n" + hatName + " contains:\n";

    for(int i = 0; i < rabbits.length; i++)
      hatString += "\t" + rabbits[i] + " ";  // Add the rabbits strings
    return hatString;
  }
  private String hatName;       // Name of the hat
  private Rabbit rabbits[];     // Rabbits in the hat

  // Nested class to define a rabbit
  class Rabbit {
    private String name;                       // Name of the rabbit

    // Constructor for a rabbit
    public Rabbit() {
      int index = select.nextInt(rabbitNames.length);  // Get random name index
      name = rabbitNames[index] + (++rabbitNamesCount[index]);
    }

    // String representation of a rabbit
    public String toString() {
      return name;
    }
  }
}

The only changes are the deletion of the static keyword in the definition of the Rabbit class – the data members relating to rabbit names have been moved to the MagicHat class. You can run this with the same version of TryNestedClass and it should produce output much the same as before.

How It Works

Although the output is much the same, what is happening is distinctly different. The Rabbit objects that are created in the MagicHat constructor are now associated with the current MagicHat object that is being constructed. The Rabbit() constructor call is actually this.Rabbit().

Using a Nested Class outside the Top-Level Class

You can create objects of an inner class outside the top-level class containing the inner class. As we discussed, how you do this depends on whether the nested class is a static member of the enclosing class. With the first version of our MagicHat class, with a static Rabbit class, you could create an independent rabbit by adding the following statement to the end of main():

System.out.println("An independent rabbit: " + new MagicHat.Rabbit());

This Rabbit object is completely free – there is no MagicHat object to restrain it. In the case of a non-static Rabbit class, things are different. Let's try this using a modified version of the previous program.

Try It Out – Free Range Rabbits (Almost)

We can see how this works by modifying the method main() in TryNestedClass to create another MagicHat object, and then create a Rabbit object for it:

static public void main(String[] args) {
  // Create three magic hats and output them
  System.out.println(new MagicHat("Gray Topper"));
  System.out.println(new MagicHat("Black Topper"));
  System.out.println(new MagicHat("Baseball Cap"));

  MagicHat oldHat = new MagicHat("Old hat");      // New hat object
  MagicHat.Rabbit rabbit = oldHat.new Rabbit();   // Create rabbit object
  System.out.println(oldHat);                     // Show the hat
  System.out.println("\nNew rabbit is: " + rabbit); // Display the rabbit
}

The output produced is:

Gray Topper contains:
    Thumper1 

Black Topper contains:
    Moppsy1     Thumper2     Thumper3 

Baseball Cap contains:
    Floppsy1     Floppsy2     Thumper4 

Old hat contains:
    Floppsy3     Thumper5     Thumper6     Thumper7     Thumper8 

New rabbit is: Thumper9

How It Works

The new code first creates a MagicHat object, oldHat. This will have its own rabbits. We then use this object to create an object of the class MagicHat.Rabbit. This is how a nested class type is referenced – with the top-level class name as a qualifier. You can only call the constructor for the nested class in this case by qualifying it with a MagicHat object name. This is because a non-static nested class can refer to members of the top-level class – including instance members. Therefore, an instance of the top-level class must exist for this to be possible.

Note how the top-level object is used in the constructor call. The object name qualifier goes before the keyword new which precedes the constructor call for the inner class. This creates an object, rabbit, in the context of the object oldHat. This doesn't mean oldHat has rabbit as a member. It means that, if top-level members are used in the inner class, they will be the members for oldHat. You can see from the example that the name of the new rabbit is not part of the oldHat object, although it is associated with oldHat. You could demonstrate this by modifying the toString() method in the Rabbit class to:

public String toString() {
  return name + " parent: "+hatName;
}

If you run the program again, you will see that when each Rabbit object is displayed, it will also show its parent hat.

Local Nested Classes

You can define a class inside a method – where it is called a local nested class. It is also referred to as a local inner class, since a non-static nested class is often referred to as an inner class. You can only create objects of a local inner class locally – that is, within the method in which the class definition appears. This is useful when the computation in a method requires the use of a specialized class that is not required or used elsewhere.

A local inner class can refer to variables declared in the method in which the definition appears, but only if they are final.

Previous Next
JavaScript Editor Java Tutorials Free JavaScript Editor