Java 101: Classes within classes

Learn the basics of nested top-level classes and inner classes

1 2 3 Page 3
Page 3 of 3

AnonymousInnerClassDemo1's use of a superclass constructor to initialize the resulting object's superclass layer begs the following question: can I declare my own constructors in an anonymous class? The answer is no. Because a constructor requires a class name and because an anonymous class has no name, how could the compiler choose a name?

Caution
Attempts to declare constructors in an anonymous inner class fail because constructors must have the names of the classes in which they appear and anonymous inner classes have no names.

Instead of constructors, you can use an object block initializer to perform custom initialization when creating an object from an anonymous inner class. For example, suppose you want to customize the Farmer Jane Doe milks cows message in AnonymousInnerClassDemo1's anonymous subclass of Farmer. You want to pass the number of cows to be milked on the command line and have that value appear in the message. Because an object block initializer executes during object creation, you simply perform the appropriate command line argument initialization in an object block initializer, as Listing 8 demonstrates:

Listing 8. AnonymousInnerClassDemo2.java

// AnonymousInnerClassDemo2.java
abstract class Farmer
{
   protected String name;
   Farmer (String name)
   {
      this.name = name;
   }
   abstract void occupation ();
}
class BeefFarmer extends Farmer
{
   BeefFarmer (String name)
   {
      super (name);
   }
   void occupation ()
   {
      System.out.println ("Farmer " + name + " raises beef cattle");
   }
}
class AnonymousInnerClassDemo2
{
   public static void main (final String [] args)
   {
      BeefFarmer bf = new BeefFarmer ("John Doe");
      bf.occupation ();
      new Farmer ("Jane Doe")
          {
              private String count;
              {
                 if (args.length == 1)
                     count = args [0];
              }
              void occupation ()
              {
                 if (count == null)
                     System.out.println ("Farmer " + name + " milks cows");
                 else
                     System.out.println ("Farmer " + name + " milks " +
                                         count + " cows");
              }
          }.occupation ();
   }
}

Assuming you type java AnonymousInnerClassDemo2 10 at the command line, you receive the following output:

Farmer John Doe raises beef cattle
Farmer Jane Doe milks 10 cows

AnonymousInnerClassDemo2 still initializes the resulting object's Farmer layer, by calling constructor Farmer (String name) with Jane Doe as the object value that name references. However, the resulting object's anonymous layer also has a chance to initialize, through the object block initializer.

Note
Even though an anonymous inner class has no name, the compiler still needs to generate a name for a class file. It turns out that the compiler chooses integer numbers, which append to enclosing class names and dollar sign characters, for anonymous inner class names. For example, in AnonymousInnerClassDemo2, the compiler generates AnonymousInnerClassDemo2.class as the class file name for the anonymous inner class.

Before you leave this section, consider the following practical application of anonymous inner classes: Developers often use anonymous inner classes to simplify event-handling -- that is, notifications of significant activities, such as moving the mouse or pressing a key -- in programs that generate and display graphical user interfaces (GUIs). Using an anonymous inner class for event handlers proves convenient because they often do not require class names. Listing 9 presents an example where an anonymous inner class facilitates the handling of the window-closing event:

Listing 9. AnonymousInnerClassDemo3.java

// AnonymousInnerClassDemo3.java
import java.awt.*;
import java.awt.event.*;
class AnonymousInnerClassDemo3
{
   public static void main (String [] args)
   {
      // Create a rectangular frame window with a title bar at the top.
      Frame f = new Frame ("Anonymous Inner Class Demo #3");
      // Add a window listener that will generate a window closing event
      // in response to user attempts to click the little X button (on
      // Windows platforms) to the right of the title bar. When the user
      // clicks that button, the window closing event results in a call
      // to a method named windowClosing().  By calling System.exit (0);
      // from within that method, the application exits.
      f.addWindowListener (new WindowAdapter ()
                           {
                               public void windowClosing (WindowEvent e)
                               {
                                  System.exit (0);
                               }
                           });
      // Establish the frame window's size as 300 horizontal pixels by
      // 100 vertical pixels.
      f.setSize (300, 100);
      // Display the frame window and get the underlying event handling
      // system running.
      f.setVisible (true);
   }
}

When run, AnonymousInnerClassDemo3 displays a rectangular window, known as a frame window, because it is the resulting GUI's main window. Once the frame window appears, however, the user should be able to remove that window and stop AnonymousInnerClassDemo3's execution. That termination typically occurs in response to the user clicking a little button, labeled X, to the top right of the window's title bar -- at least on Windows platforms.

When the user clicks X, the underlying Java windowing toolkit creates an event object and calls a special method -- windowClosing (WindowEvent e), where e contains a reference to that event object -- in an object known as the frame window's window listener. That listener object registers with the underlying toolkit (so the toolkit knows the method location) by calling Frame's addWindowListener (WindowListener wl) method.

The WindowListener interface declares several methods, one method for each potential window event. To spare the developer from having to supply code for all those methods, Java's windowing toolkit designers built a WindowAdapter class that implements all WindowListener methods. Because those implemented methods are empty stubs, the developer declares an anonymous inner class that extends WindowAdapter and overrides one or more of those methods with specific code. As you can see, I have chosen to override windowClosing (WindowEvent e) and have that method call System.exit (0); to terminate the program (which also closes the window), in response to a call made by the windowing toolkit to windowClosing (WindowEvent e).

Note
You will often work with anonymous inner classes when developing event handlers for your GUIs. In future articles, I will provide many examples of event handlers built from anonymous inner classes.

Review

This article has shown class nesting to be a useful addition to the Java language. Not only does class nesting help clarify source code -- because you can declare classes closer to the objects they manipulate -- it also helps reduce the number of conflicts between the names of classes declared at the same level within a source file.

There are four categories of nested classes: nested top-level classes, instance inner classes, local inner classes, and anonymous inner classes. Nested top-level classes can only access the class fields and call the class methods in any enclosing class. In contrast, instance inner classes can access an enclosing class's class and instance fields, and call an enclosing class's class or instance methods. Because nested top-level classes and instance inner classes can only appear within the confines of another class, Java supplies local inner classes that can appear within any block -- including a method block or the block following an if statement. Furthermore, because some local inner classes are so short they do not warrant their own class names, Java supplies anonymous inner classes. Local and anonymous inner classes can access the local variables and parameters accessible within their enclosing blocks, provided that you mark those local variables and parameters final.

I encourage you to email me with any questions you might have involving either this or any previous article's material. (Please, keep such questions relevant to material discussed in this column's articles.) Your questions and my answers will appear in the relevant study guides.

In next month's article, you will learn about exceptions and how to handle them.

Jeff Friesen has been involved with computers for the past 20 years. He holds a degree in computer science and has worked with many computer languages. Jeff has also taught introductory Java programming at the college level. In addition to writing for JavaWorld, he has written his own Java book for beginners -- Java 2 By Example, Second Edition (Que Publishing, 2001; ISBN: 0789725932) -- and helped write Special Edition Using Java 2 Platform (Que Publishing, 2001; ISBN: 0789724685). Jeff goes by the nickname Java Jeff (or JavaJeff). To see what he's working on, check out his Website at http://www.javajeff.com.

Learn more about this topic

1 2 3 Page 3
Page 3 of 3