Section 6.4
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 in 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, something 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

Note that 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. In general, non-static members of a class are not really part of the class itself. Instead, they become part of the 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.

In any case, any object belonging to a non-static nested class is permanently associated with some particular object belonging to the containing class, and the nested class object has complete access to the members of the containing class object. This means that in the definition of the non-static nested class, you can use any member of the containing class.

Looking at an example will help. Here is a simple applet that shows four randomly colored squares. Each square is actually a Canvas object. The applet runs a thread that every so often picks one of the squares and changes its color:

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, SUSPEND = 1, TERMINATE = 2;  // Possible values for status.
         
      
         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.
               while (status != TERMINATE) {
                  synchronized(this) {
                     while (status == SUSPEND)  // wait while applet is suspended
                        try { wait(); }
                        catch (InterruptedException e) {  }
                  }
                  switch ( (int)(4*Math.random()) ) {  // change the color of a canvas
                     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 {
               synchronized(runner) {
                  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 much 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, 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 applet object that is associated with the thread? Is there any way to refer to that 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.


Now, you might be wondering what all this has to do with components and events. Well, event-handling is a 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 might 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
                  }
              } // 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, 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
                    }
                    // 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 it might be better to use 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
              }

The intention of this expression is: "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. Since the anonymous class is a subclass of ComponentAdapter, that object also belongs to the ComponentAdapter class, by inheritance. So the newly created object can be passed to the addComponentListener() method.

Note that it is also 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(  // set up an object to listen for mouse clicks
                        new MouseAdapter() { 
                               public void mousePressed(MouseEvent evt) {
                                  randomColor();  // change color when mouse is pressed
                               }
                            }
                 );  // 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

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