Instantiating Local Classes – Nested Type Declarations

Instantiating Local Classes

Clients outside the scope of a local class cannot instantiate the class directly because such classes are, after all, local. A local class can be instantiated in the block in which it is defined. Like a local variable, a local class must be declared before being used in the block.

A method can return instances of any local class it declares. The local class type must then be assignable to the return type of the method. The return type cannot be the same as the local class type, since this type is not accessible outside the method. A supertype of the local class must be specified as the return type. This also means that, in order for the objects of the local class to be useful outside the method, a local class should implement an interface or override the behavior of its supertypes.

Example 9.12 illustrates how clients can instantiate local classes. The nesting and the inheritance hierarchy of the classes involved are shown in Figure 9.5. The non-static local class Circle at (5) is defined in the non-static method createCircle() at (4), which has the return type Shape. The static local class Graph at (9) is defined in the static method createGraph() at (8), which has the return type IDrawable.

Figure 9.5 Local Classes and Inheritance Hierarchy

Example 9.12 Instantiating Local Classes

Click here to view code image

// File: LocalClassClient.java
interface IDrawable {                     // (1)
  void draw();
}
//_____________________________________________________________________________
class Shape implements IDrawable {        // (2)
  @Override
  public void draw() { System.out.println(“Drawing a Shape.”); }
}
//_____________________________________________________________________________
class Painter {                           // (3) Top-level Class
  public Shape createCircle(final double radius) { // (4) Non-static Method
    class Circle extends Shape {          // (5) Non-static local class
@Override
      public void draw() {
        System.out.println(“Drawing a Circle of radius: ” + radius); // (6)
      }
    }
    return new Circle();                  // (7) Passed enclosing object reference
  }
  public static IDrawable createGraph() { // (8) Static Method
    class Graph implements IDrawable {    // (9) Static local class
      @Override
      public void draw() { System.out.println(“Drawing a Graph.”); }
    }
    return new Graph();                   // (10) Object of static local class
  }
}
//_____________________________________________________________________________
public class LocalClassClient {
  public static void main(String[] args) {
    IDrawable[] drawables = {             // (11)
        new Painter().createCircle(5),    // (12) Object of non-static local class
        Painter.createGraph(),            // (13) Object of static local class
        new Painter().createGraph()       // (14) Object of static local class
    };
    for (IDrawable aDrawable : drawables) // (15)
      aDrawable.draw();
    System.out.println(“Local Class Names:”);
    System.out.println(drawables[0].getClass().getName());   // (16)
    System.out.println(drawables[1].getClass().getName());   // (17)
  }
}

Output from the program:

Click here to view code image

Drawing a Circle of radius: 5.0
Drawing a Graph.
Drawing a Graph.
Local Class Names:
Painter$1$Circle
Painter$1$Graph

The main() method in Example 9.12 creates a polymorphic array drawables of type IDrawable[] at (11), which is initialized at (12) through (14) with instances of the local classes.

Creating an instance of a non-static local class requires an instance of the enclosing class. In Example 9.12, the non-static method createCircle() is invoked on the instance of the enclosing class Painter to create an instance of the non-static local class Circle in the non-static method, as shown at (12). The reference to the instance of the enclosing class is passed implicitly in the constructor call at (7) to the non-static local class.

A static method can be invoked either through the class name or through a reference of the class type. An instance of a static local class can be created either way by calling the createGraph() method, as shown at (13) and (14). As might be expected in static context, no outer object is involved.

As references to a local class cannot be declared outside the local context, the functionality of the class is only available through supertype references. The method draw() is invoked on objects in the array IDrawable at (15). The program output indicates which objects were created. In particular, note that the final parameter radius of the method createCircle() at (4) is accessed in the draw() method of the local class Circle at (6). An instance of the local class Circle is created at (12) by a call to the method createCircle(). The draw() method is invoked on this instance of the local class Circle in the loop at (15). The value of the final parameter radius is still accessible to the draw() method invoked on this instance, although the call to the method createCircle(), which created the instance in the first place, has completed. Values of (effectively) final local variables continue to be available to instances of local classes whenever these values are needed.

The output in Example 9.12 also shows the actual names of the local classes. In fact, the local class names are reflected in the generated class file names. Because multiple local class declarations with the same name can be defined in the methods of the enclosing class, a numbering scheme ($i) is used to generate distinct class file names.

Leave a Reply

Your email address will not be published. Required fields are marked *