Section 7.6
Nested Classes and Adapter Classes


A CLASS SEEMS LIKE IT SHOULD BE a pretty important thing. A class is a high-level building block of a program, representing a potentially complex idea and its associated data and behaviors. I've always felt a bit silly writing tiny little classes that exist only to group a few scraps of data together. However, such trivial classes are often useful and even essential. Fortunately, in Java, I can ease the embarrassment, because one class can be nested inside another class. My trivial little class doesn't have to stand on its own. It becomes part of a larger more respectable class. This is particularly useful when you want to create a little class specifically to support the work of a larger class. And, more seriously, there are other good reasons for nesting the definition of one class inside another class.

In Java, a nested or inner class is any class whose definition is inside the definition of another class. Inner classes can be either named or anonymous. I will come back to the topic of anonymous classes later in this section. A named inner class looks just like any other class, except that it is nested inside another class. (It can even contain further levels of nested classes, but you shouldn't carry these things too far.)

Like any other item in a class, a named inner class can be either static or non-static. A static nested class is part of the static structure of the containing class. It can be used inside that class to create objects in the usual way. If it has not been declared private, then it can also be used outside the containing class, but when it is used outside the class, its name must indicate its membership in the containing class.

For example, suppose a class named WireFrameModel represents a set of lines in three-dimensional space. (Such models are used to represent three-dimensional objects in graphics programs.) Suppose that the WireFrameModel class contains a static nested class, Line, that represents a single line. Then, outside of the class WireFrameModel, the Line class would be referred to as WireFrameModel.Line. Of course, this just follows the normal naming convention for static members of a class. The definition of the WireFrameModel class with its nested Line class would look, in outline, like this:

            public class WireFrameModel {
            
               . . . // other members of the WireFrameModel class
               
               static public class Line {
                     // Represents a line from the point (x1,y1,z1)
                     // to the point (x2,y2,z2) in 3-dimensional space.
                  double x1, y1, z1;
                  double x2, y2, z2;
               } // end class Line
               
               . . . // other members of the WireFrameModel class
               
            } // end WireFrameModel

Inside the WireframeModel class, a Line object would be created with the constructor "new Line()". Outside the class, "new WireFrameModel.Line()" would be used.

A static nested class has full access to the members of the containing class, even to the private members. This can be another motivation for declaring a nested class, since it lets you give one class access to the private members of another class without making those members generally available to other classes.

When you compile the above class definition, two class files will be created. Even though the definition of Line is nested inside WireFrameModel, the compiled Line class is stored in a separate file. The name of the class file for Line will be WireFrameModel$Line.class.

To understand non-static nested classes, you have to stretch your mind a bit. Non-static members of a class are not really part of the class itself. This is just as true for non-static nested classes as it is for any other non-static part of a class. The non-static members of a class specify what will be contained in objects that are created from that class. The same is true -- at least logically -- for non-static nested classes. It's as if each object that belongs to the containing class has its own copy of the nested class. For example, from outside the containing class, a non-static nested class has to be referred to as objectName.NestedClassName, rather than as ContainingClassName.NestedClassName. The non-static nested class cannot be used in the static methods of the containing class. It can, of course, be used in the non-static methods of the containing class -- and in that case, what's being used is really the copy of the nested class associated with the "this" object in that method, as if you had said "this.NestedClassName".

In order to create an object that belongs to a non-static nested class, you must first have an object that belongs to the containing class. The nested class object is permanently associated with the containing class object, and it has complete access to the members of the containing class object. Looking at an example will help. Here is a simple applet that shows four randomly colored squares. Each square is a Canvas object. The applet runs a thread that every so often picks one of the squares and changes its color:

Sorry, but your browser
doesn't support Java.

The thread in this applet could have been programmed by declaring that the applet class "implements Runnable" and adding a run() method to the class. Instead of doing it that way, however, I decided to define a non-static nested class to represent the thread object. The nested class is a subclass of the Thread class, and it has its own run() method. Because of the nesting, the thread object has access to the instance variables of the applet object, so communication between the thread and the applet is no problem. Here's the full definition of the applet class.

      public class RandomColorGrid extends Applet {
      
         ColorSwatchCanvas canvas0, canvas1, canvas2, canvas3;  
             // Canvases to be displayed in applet.  (The definition 
             // of the ColorSwatchCanvas class will be given below.)
         
         Thread runner;   // A thread that randomly changes the color 
                          // of a canvas every so often.  Note that all 
                          // synchronization in this applet is 
                          // done on the Thread object.
      
         volatile int status; // Status variable for controlling the thread.
         
         final static int GO = 0,          // Possible values for status.
                          SUSPEND = 1, 
                          TERMINATE = 2; 
         
      
         class Runner extends Thread {
         
              // The runner for the RandomColorGrid canvas will be an
              // object belonging to this nested class.
              
            public void run() {
                  // Run method picks a random canvas, changes it's color, 
                  // waits for a random time between 30 and 3029 
                  // milliseconds, and then repeats this process
                  // indefinitely.
               while (status != TERMINATE) {
                  synchronized(this) {
                       // As long as applet is stopped, don't do anything.
                     while (status == SUSPEND) 
                        try { wait(); }
                        catch (InterruptedException e) {  }
                  }
                  switch ( (int)(4*Math.random()) ) { 
                     case 0: canvas0.randomColor(); break;
                     case 1: canvas1.randomColor(); break;
                     case 2: canvas2.randomColor(); break;
                     case 3: canvas3.randomColor(); break;
                  }
                  synchronized(this) {  // delay for a bit
                    try { wait( (int)(3000*Math.random() + 30) ); }
                    catch (InterruptedException e) { } 
                  }
               }
            } // end run()
      
         } // end nested class Runner
      
      
         public void init() {
               // Initialize the applet.  Create 4 ColorSwatchCanvasses
               // and arrange them in a horizontal grid.
            setBackground(Color.black);
            setLayout(new GridLayout(1,0,2,2));
            canvas0 = new ColorSwatchCanvas();
            canvas1 = new ColorSwatchCanvas();
            canvas2 = new ColorSwatchCanvas();
            canvas3 = new ColorSwatchCanvas();
            add(canvas0);
            add(canvas1);
            add(canvas2);
            add(canvas3);
         }
      
         public Insets getInsets() {
              // Put a 2-pixel black border around the applet.
            return new Insets(2,2,2,2);
         }
      
         public void start() {
               // Applet is being started or restarted.  Create a 
               // thread or tell the existing thread to restart.
            status = GO;
            if (runner == null || ! runner.isAlive()) {
               runner = new Runner();
               runner.start();
            }
            else {
               runner.notify();
            }
         }
      
         public void stop() {
               // Applet is about to be stopped.  Tell thread to suspend.
            synchronized(runner) {
               status = SUSPEND;
               runner.notify();
            }
         }
      
         public void destroy() {
               // Applet is about to be destroyed.
               // Tell thread to terminate.
            synchronized(runner) {
               status = TERMINATE;
               runner.notify();
            }
         }
         
      }  // end class RandomColorGridApplet

This is actually a cleaner way of programming threads than willy-nilly declaring all kinds of objects to be Runnable.

Note, by the way, how I did the synchronization in this example. Synchronization only works if all concerned threads are synchronized on the same object. In the run() method, synchronization is done on "this", which refers to the thread object. If I had simply declared start(), stop(), and destroy() to be synchronized methods, they would be synchronized on the applet object, not the thread object, which would do me no good at all. So instead, I use a synchronized statement to synchronize on the thread object, runner.

The use of the special variable, this, in the run() method raises another issue. When used in the Runner class, this refers to the object of type Runner. What about the RandomColorGridApplet object that is associated with the thread? Is there any way to refer to the applet object? Yes, but it's a little clumsy. Inside the nested Runner class, the applet object that is associated with the thread object, this, is referred to as RandomColorGridApplet.this.


Event-handling is an even more natural application of nested classes. Instead of adding the responsibility of listening for events onto an object that already has some other well-defined responsibility, you can create an object belonging to a nested class that has handling certain events as its one-and-only, clearly-defined responsibility. Since the event-handling object belongs to a nested class, it has access to any data and methods that it needs from the containing class.

Here is an outline of how you could use a nested class to handle action events from a button:

           public class MyApplet extends Applet {
        
              class ButtonHandler implements ActionListener {
                  public void actionPerformed(ActionEvent evt) {
                     . . . // Handle the action event.
                           // (This method has full access to all
                           // the members of the MyApplet class.)
                  }
              } // end nested class ButtonHandler
           
              public void init() {
                 ButtonHandler handler = new ButtonHandler();
                 Button bttn = new Button("Do It!");
                 bttn.addActionListener(handler);
                 . . . // other initialization
              }
           
              . . . // other members of MyApplet
           
           } // end class MyApplet
           

Some of the listener interfaces, such as MouseListener and ComponentListener, include a lot of methods. The rules for interfaces say that when a class implements an interface, it must include a definition for each method declared in the interface. For example, if you are only be interested in using the mousePressed() method of the MouseListener interface, you end up including empty definitions for mouseClicked(), mouseReleased(), mouseEntered(), and mouseExited(). If you use a specially-created nested class to handle events, there is a way to avoid this. As a convenience, the package java.awt.event includes several adapter classes, such as MouseAdapter and ComponentAdapter. The MouseAdapter class is a trivial class that implements the MouseListener interface by defining each of the methods in that interface to be empty. To make your own mouse listener classes, you can extend the MouseAdapter class and override just those methods that you are interested in. ComponentAdapter and other adapter classes work in the same way.

For example, if you want to respond to changes in the size of a component, you could use a component listener based on ComponentAdapter to listen for componentResized events:

       public class MyApplet extends Applet {
       
          class Resizer extends ComponentAdapter {
              public void componentResized(ComponentEvent evt) {
                 . . . // Respond to new component size.
                       // (This method has full access to all
                       // the members of MyApplet.)
              }
              // No need to define the other ComponentListener Methods
          } // end nested class Resizer
          
          public void init() {
             MyCanvas canvas = new MyCanvas();  // Some component in whose
                                                // size we are interested.
             canvas.addComponentListener( new Resizer() );
             . . . // more initialization
          }

          . . . // more members of MyApplet
          
       } end class MyApplet
             

In some cases, you might find yourself writing a nested class and then using that class in just a single line of your program. For example, the Resizer class in the above example might well be used only in the line "canvas.addComponentListener(new Resizer());". Is it worth creating such a class? Indeed, it can be, but for cases like this you have the option of using an anonymous nested class. An anonymous class is created with a variation of the new operator that has the form

          new  superclass-or-interface () {
                   methods-and-variables
              }

This constructor defines a new class, without giving it a name, and it simultaneously creates an object that belongs to that class. The intention of this expression is to refer to: "a new object of a class that is the same as superclass-or-interface but with these methods-and-variables added." An expression of this form can be used in any statement where a regular "new" could be used. For example:

       canvas.addComponentListener(
                new ComponentAdapter() {
                     public void componentResized(ComponentEvent evt) {
                         . . . // Respond to new component size.
                     }
                  } // end of anonymous class definition
            );  // end of canvas.addComponentListener statement

This defines an anonymous class that is a subclass of ComponentAdapter, containing the given componentResized() method. It also creates an object belonging to that anonymous class. That object is assigned to be the listener for component events from the canvas. When the canvas changes size, the object's componentResized() method is called.

Note that it is possible to base an anonymous class on an interface, rather than a superclass. In this case, the anonymous class must implement the interface by defining all the methods that are declared in the interface.

For a final example, we'll return to the colored-square applet from earlier on this page. In addition to the spontaneous color changes in that applet, each of the colored squares will change color if you click on it. The squares are objects belonging to a class named ColorSwatchCanvas. This class sets up a mouse listener to listen for clicks on the canvas. It does this with an anonymous class:

      class ColorSwatchCanvas extends Canvas {
      
           // A canvas that displays a random color.  It changes to
           // another random color if the user clicks on it, or if the 
           // randomColor()  method is called.
      
         ColorSwatchCanvas() {  // constructor
         
           randomColor();  // Select the canvas's initial random color.
         
           addMouseListener(  // Create an object to listen for mouse clicks.
                    new MouseAdapter() { 
                           public void mousePressed(MouseEvent evt) {
                                 // Change color when mouse is pressed.
                              randomColor();  
                           }
                        }
                 );  // end addMouseListener statement
                 
         } // end constructor
      
         void randomColor() {
               // Change the color of the canvas to a random color.
            int r = (int)(256*Math.random());
            int g = (int)(256*Math.random());
            int b = (int)(256*Math.random());
            setBackground(new Color(r,g,b));
            repaint();
         }
         
      }  // end class ColorSwatchCanvas
      

Using anonymous nested classes is generally considered to the best programming style for event handling.


[ Next Section | Previous Section | Chapter Index | Main Index ]