October 20, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

The Essence of OOP using Java, Anonymous Classes

  • January 20, 2004
  • By Richard G. Baldwin
  • Send Email »
  • More Articles »

Java Programming Notes # 1640


Preface

This series of lessons is designed to teach you about the essence of Object-Oriented Programming (OOP) using Java.

The first lesson in the series was entitled The Essence of OOP Using Java, Objects, and Encapsulation.  The previous lesson was entitled The Essence of OOP using Java, Local Classes.

You may find it useful to open another copy of this lesson in a separate browser window.  That will make it easier for you to scroll back and forth among the different figures and listings while you are reading about them.

For further reading, see my extensive collection of online Java tutorials at Gamelan.com. A consolidated index is available at www.DickBaldwin.com.

Preview

What can you include in a class definition?

There are several different kinds of items that can be included in a class definition.  As you learned in the earlier lessons in this series, the list includes:

  • Static variables
  • Instance variables
  • Static methods
  • Instance methods
  • Constructors
  • Static initializer blocks
  • Instance initializers

Can also contain other class definitions

As you also learned in previous lessons, a class definition can also contain the following four kinds of inner classes:

  • Member classes
  • Local classes
  • Anonymous classes
  • Nested top-level classes and interfaces

The previous two lessons explained member classes and local classes.  This lesson will explain anonymous classes.  The next lesson will explain nested top-level classes and interfaces.

(Note that it is questionable whether a nested top-level class or interface should be referred to as an inner class, because an object of a nested top-level class can exist in the absence of an object of the enclosing class.  Regardless of whether the term inner class applies, a nested top-level class is defined within the definition of another class, so its definition is internal to the definition of another class.)

What is an anonymous class?

I'm going to begin my discussion with a quotation from one of my favorite authors, David Flanagan, author of Java in a Nutshell.

"An anonymous class is essentially a local class without a name."

If you have read the previous lesson, you should know quite a lot about local classes at this point in time.  Continuing with Flanagan's words,

"Instead of defining a local class and then instantiating it, you can often use an anonymous class to combine these two steps...  an anonymous class is defined by a Java expression, not a Java statement.  This means that an anonymous class definition can be included within a larger Java expression..."

As you will see from the sample program in this lesson, anonymous class definitions are often included as arguments to method calls.

As is the case for an object of a member class or a local class (discussed in previous lessons), an object of an anonymous class must be internally linked to an object of the enclosing class.

Thus, an anonymous class is truly an inner class, because an object of the anonymous class cannot exist in the absence of an object of the enclosing class.

What about an anonymous interface?

Interfaces defined within classes are implicitly static.  This means that they are always top-level.  There is no such thing as a member interface, a local interface, or an anonymous interface.

Why use anonymous classes?

As with local classes, objects instantiated from anonymous classes share many of the characteristics of objects instantiated from member classes.  However, in some cases, an anonymous class can be defined closer to its point of use than would be possible with a member class or a local class.  Once you become accustomed to the somewhat cryptic syntax used with anonymous classes, this can often lead to improved code readability.

Probably the most important benefit of anonymous classes has to do with accessing the members of enclosing classes.  Just like with member classes and local classes, methods of an anonymous class have direct access to all the members of the enclosing classes, including private members.  Thus the use of anonymous classes can often eliminate the requirement to connect objects together via constructor parameters.

In addition, although not demonstrated in this lesson, as with local classes, objects of anonymous classes have access to final local variables that are declared within the scope of the anonymous class.

Can be particularly useful when ...

An anonymous class can be particularly useful in those cases where

  • There is no reason for an object of the anonymous class to exist in the absence of an object of the enclosing class.
  • There is no reason for an object of the anonymous class to exist outside a method of the enclosing class.
  • Methods of the object of the anonymous class need access to members of the object of the enclosing class.
  • Methods of the object of the anonymous class need access to final local variables and method parameters belonging to the method in which the anonymous class is defined.
  • Only one instance of the anonymous class is needed.
  • There is no need for the class to have a name that is accessible elsewhere in the program.

Purpose of this lesson

This lesson explains anonymous classes from a practical viewpoint, including a comparison between anonymous classes and local classes.

Anonymous classes versus local classes

Once again, according to David Flanagan,

"...an anonymous class behaves just like a local class, and is distinguished from a local class merely in the syntax used to define and instantiate it."

Unlike a local class, however, an anonymous class cannot define a constructor.  An anonymous class can define an instance initializer, which can provide some of the benefits of a constructor.

(I discussed instance initializers in detail in an earlier lesson entitled The Essence of OOP using Java, Instance Initializers.  As you may recall, a primary shortcoming of an instance initializer as compared to a constructor is that an instance initializer cannot accept incoming parameters.)

Restrictions on the use of anonymous classes

Because an anonymous class has no name, and the definition and instantiation of the class appear in a single expression, only one instance of each anonymous class can be created.  If you need more than one instance of the class, you should probably use a local class, a member class, or a top-level class instead.

As mentioned above, it is not possible to define constructors for anonymous classes.  If you need to use a constructor when you instantiate the class, you should probably use a local class, a member class, or a top-level class instead.

As with member classes and local classes, anonymous classes cannot contain static members.

As with local variables and local classes, anonymous classes cannot be declared public, protected, private, or static.  In fact, no modifiers can be specified in the definition of an anonymous class.

Smoke and mirrors

As I told you in my earlier lessons on local classes, the methods in an anonymous class don't really have access to local variables and method parameters.  Rather, when an object of the anonymous class is instantiated, copies of the final local variables and method parameters referred to by the object's methods are stored as instance variables in the object.  The methods in the object of the anonymous class really access those hidden instance variables.

Thus, the local variables and method parameters accessed by the methods of the local class must be declared final to prevent their values from changing after the object is instantiated.

There are some additional activities involving smoke and mirrors taking place behind the scenes when you define and instantiate an anonymous class.  Generally speaking, this involves the automatic generation of code to cause things to behave as they do.  The good news is that you don't have to write that extra code, and you don't have to maintain it.  The extra code is written for you, and if you modify your class structure, the extra code is automatically modified accordingly.

You can read about the code that is automatically generated in my earlier lessons on local classes and member classes.

Syntax for anonymous classes

Before getting into actual code in the sample program, I want to explain the syntax used to define and instantiate an anonymous class.

The definition and instantiation of an anonymous class uses one or the other of the two expressions shown in Figure 1.

new className(optional argument list){classBody}

new interfaceName(){classBody}
Figure 1

Usually, this expression is included inside a larger overall expression, such as an argument to a method call.

What does the first expression mean?

Here is how I usually explain this syntax to my students.  The first expression in Figure 1 starts out fairly normal, but becomes cryptic very quickly.  This expression instantiates a new object from an unnamed and previously undefined class, which automatically extends the class named className, and which cannot explicitly implement any interfaces.  The body of the new class is given by classBody.

The result of executing this expression is that a new class that extends className is defined, a new object of the new class is instantiated, and the expression is replaced by a reference to the new object.

Example usage

If this expression appears as the right operand of an assignment operator, the object's reference is saved in the left operand of the assignment operator.   If the expression appears as an argument in a method call, the object's reference is passed to the method.  If the expression appears in some other form of larger overall expression, the object's reference is handed over to the surrounding expression to be used appropriately.

What about instantiating an interface?

The second expression in Figure 1 starts out weird.  To my knowledge, there is no other situation in Java where you apply the new operator to the name of an interface.  From the beginning, you have been told that you cannot instantiate an object of an interface.  (An interface is implicitly abstract and it doesn't have a constructor, not even a default constructor.)  However, you can instantiate an object of a class that implements none, one, or more interfaces.

The correct interpretation of the second expression in Figure 1 is as follows.   This expression instantiates a new object from an unnamed and previously undefined class, which automatically implements the interface named interfaceName, and automatically extends the class named Object.   The class can explicitly implement one, and only one interface, and cannot extend any class other than Object.  Once again, the body of the new class is given by classBody.

As in the case of the first expression in Figure 1, the result of executing this expression is that a new class that implements interfaceName is defined, a new object of the new class is instantiated, and the expression is replaced by a reference to the new object.  That reference is handed over to the surrounding expression to be used appropriately.

What about constructor parameters?

As mentioned earlier in this lesson, since the new class doesn't have a name, it isn't possible to define a constructor for the new class.  According to Flanagan,
"Any arguments you specify between the parentheses following the superclass name in an anonymous class definition are implicitly passed to the superclass constructor."
Thus, it is possible to define an anonymous class that extends a class whose constructor requires parameters, and to pass those parameters to the superclass constructor when the anonymous class is instantiated.

The parentheses following interfaceName in the second expression in Figure 1 must always be empty.  In this case, the superclass is always Object, which never expects constructor parameters.

Enough talk, let's see some code

The paragraphs that follow will explain a program named InnerClasses08, which is designed specifically to illustrate anonymous classes, and to compare anonymous classes with local classes.  I will discuss the program in fragments.  A complete listing of the program is provided in Listing 10 near the end of the lesson.

Discussion and Sample Code

When the program is executed, it produces the GUI shown in Figure 2.  I will refer back to this figure during the discussion of the program.

Program GUI

Figure 2  Program GUI

Class file names

This program consists of a total of six classes:
  • Two top-level classes
  • One local class
  • Three anonymous classes
When compiled, the program produces the class files shown in Figure 3. 

GUI$1$BaldButton.class
GUI$1.class
GUI$2.class
GUI$3.class
GUI.class
InnerClasses08.class
Figure 3

As you will see later, the file named GUI$1$BaldButton.class represents the local class.  The three files named GUI$1.class, GUI$2.class, and GUI$3.class represent anonymous classes.  The two remaining files represent top-level classes.
(As you can see, the anonymous classes are not truly anonymous, since the files that represent them must have names.  Generally, however, the establishment of the individual names is beyond the control of the programmer, and the names are not known to the program in a way that makes it possible to refer to them by name.)
Program structure and behavior

This program is designed to illustrate the use of local classes and anonymous classes in a very practical way. It illustrates one implementation of a local class and three different implementations of anonymous classes. The program compares the local class with an anonymous class designed to accomplish the same purpose.  The program also illustrates the use of instance initializers as an alternative to constructors.

A local class

The program defines and uses a local class to instantiate an object to handle mouse clicked events on a button with low-level event handling. This class uses a constructor to enable mouse events on a new extended Button class. It also uses a constructor to display the name of the class file.

An anonymous class to compare with the local class

The program also defines and uses an anonymous class to instantiate an object to handle mouse clicked events on a button with low-level event handling. This class uses an instance initializer to enable mouse events on a new extended Button class. It also uses an instance initializer to display the name of the class file.  This class and the local class described above provide a direct comparison between the use of local classes and anonymous classes to serve the same purpose.

An anonymous class that implements an interface

The program illustrates the use of an anonymous class, which implements the MouseListener interface, to instantiate an object to handle mouse clicked events using the source-listener event model (sometimes referred to as the delegation event model or the JavaBeans event model). The anonymous class uses an instance initializer to display the name of the class file.

An anonymous class that extends an existing class

The program illustrates the use of an anonymous class, which extends the WindowAdapter class, to instantiate an object to handle window events fired by the close button in the upper-right corner of the Frame object shown in Figure 2.  This class also uses the source-listener event model, and uses an instance initializer to display the name of the class file.

The screen output

The program produces the screen output shown in Figure 4 when
  • The program is started
  • Each button shown in Figure 2 is clicked once in succession, going from left to right
  • The close button in the upper-right corner of the Frame object in Figure 2 is clicked
Local class name: GUI$1$BaldButton
Anonymous class B name: GUI$1
Anonymous class C name: GUI$2
Anonymous window listener class name: GUI$3
buttonA clicked
buttonB clicked
buttonC clicked
Close button clicked
Figure 4

When the close button is clicked, the program produces the last line of text in Figure 4 and terminates.

I will identify the code that produces each line of output text in the discussion of the program that follows.

The controlling class

The controlling class for the program is shown in Listing 1.

public class InnerClasses08 {
public static void main(String[] args){
new GUI();
}//end main
}//end class InnerClasses08

Listing 1

As you can see, the controlling class is very simple, with the main method instantiating an object of the GUI class.  This results in the GUI that is pictured in Figure 2.

Local and anonymous classes inside GUI constructor

The local class and the three anonymous classes are defined inside the constructor for the GUI class.
(Recall that local classes and anonymous classes are defined inside code blocks, which often place them inside methods and constructors, but you can also place them inside static initializer blocks and instance initializers.)
The first four lines of the output text in Figure 4 are produced by constructors and instance initializers in the local and anonymous classes.  Therefore, those four lines of text are produced when the new object of the GUI class is instantiated.

The GUI class

As is often the case, the GUI class used to create the visual GUI shown in Figure 2 consists solely of a constructor.  Basically, this constructor places three buttons in the frame and registers event handlers on the buttons and on the frame.  Once the GUI object is constructed and appears on the screen, all further activity in the program occurs under control of the event handlers associated with the buttons and the frame.
(You can learn more about event handling by reviewing my online tutorial lessons at http://www.dickbaldwin.com/tocmed.htm.)
The GUI constructor

The GUI class, and the constructor for that class begin in Listing 2.

class GUI extends Frame{
public GUI(){//constructor
setLayout(new FlowLayout());
setSize(250,75);
setTitle("Copyright 2003 R.G.Baldwin");

Listing 2

As you can see, the GUI class extends Frame, so that an object of the class is a frame.

The constructor code shown in Listing 2 simply sets values for the layout, size, and title properties of the frame.

The BaldButton class

The BaldButton class, whose definition begins in Listing 3, is a local class that extends Button.  This class extends the Button class to make it possible to override the processMouseEvent method in order to handle mouse events that are fired by the button.  This is a form of low-level event handling, which will be contrasted with source-listener event handling later in the program.

Listing 3 shows the constructor for the BaldButton class.

    class BaldButton extends Button{
BaldButton(String text){//constructor
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
setLabel(text);
System.out.println("Local class name: " +
getClass().getName());
}//end constructor

Listing 3

Enable mouse events

The most important code in the constructor is the statement that enables mouse events on the button.  If you are unfamiliar with the enableEvents method, you should look it up in the Sun documentation.

Briefly, this method must be invoked on the button to cause the overridden processMouseEvent method to be invoked later when the button fires a mouse event.

The remaining constructor code

The remaining code in the constructor
  • Sets the text value on the face of the button
  • Gets and displays the name of the class file that represents this local class
The screen output

Construction of the button by the code in Listing 3 causes the text shown in Figure 5 to appear on the screen.  This is how I was able to identify the name of the class file that represents the local class in my earlier discussion of class file names.

Local class name: GUI$1$BaldButton
Figure 5

We will see later that this button will be added as the leftmost button in the GUI shown in Figure 2.

The processMouseEvent method

Continuing with the constructor for the BaldButton class, Listing 4 shows the overridden processMouseEvent method for an object of the BaldButton class.

      public void processMouseEvent(
MouseEvent e){
if (e.getID() ==
MouseEvent.MOUSE_CLICKED){
System.out.println("buttonA clicked");
}//end if

//The following is required of overridden
// processMouseEvent method.
super.processMouseEvent(e);
}//end processMouseEvent
}//end class BaldButton

//Add button to Frame
add(new BaldButton("A"));
Listing 4

This method is invoked each time an object instantiated from this class fires a mouse event.  That is why I refer to the method as an event handler for the button.

Different kinds of mouse events

A button can fire a variety of different kinds or subcategories of mouse events: 
  • MOUSE_CLICKED
  • MOUSE_DRAGGED
  • MOUSE_ENTERED
  • MOUSE_EXITED
  • MOUSE_MOVED
  • MOUSE_PRESSED
  • MOUSE_RELEASED 
In this case, I elected to ignore all but MOUSE_CLICKED.  This subcategory of mouse event occurs when a mouse button is pressed and then released.

Thus the code in the event handler of Listing 4 first confirms that the event was of the MOUSE_CLICKED variety, and if so, it displays a message that matches the fifth line of text in the output shown in Figure 4.

Invoke processMouseEvent on the superclass

Without getting into the details of why this is required, I'm simply going to tell you that when you use this low-level event model to handle events, your overridden processMouseEvent method must call the same method in the superclass, passing the incoming parameter of type MouseEvent as a parameter to the superclass version of the method.

Add a button to the frame

The last statement in Listing 4 instantiates a new BaldButton object, setting the text on the face of the button to A, and adds that new object to the frame.  Because the layout property of the frame has been set to FlowLayout, and because this is the first component added to the frame, this button appears as the leftmost button in the GUI shown in Figure 2.

Could instantiate multiple buttons of this type

Although I instantiated the button object as an anonymous object in this case, that wasn't necessary.  Using this local class, I could instantiate more than one object of this type, saving the object's references in reference variables of the appropriate type.  Later we will see that this is not possible for anonymous classes.

It is interesting to note, however, that with this event handle model, if I were to instantiate multiple buttons of this type, the same processMouseEvent method would be invoked no matter which of the buttons fired a mouse event.  If I wanted different behavior as a result of the different buttons firing mouse events, I would have to write code inside the processMouseEvent method to deal with that issue.  The source-listener event model that I will illustrate later doesn't suffer from that restriction.

An anonymous inner class for low-level event handling

Listing 5 shows the beginning of an anonymous class to perform low-level event handling similar to that shown in Listing 4.  This code defines an anonymous inner class, which extends Button, and which has mouse events enabled.  I provided this class primarily for comparison with the local class named BaldButton.  This class is an anonymous alternative to the local BaldButton class.

    add(new Button("B")
{//Begin class definition
{//Instance initializer
enableEvents(
AWTEvent.MOUSE_EVENT_MASK);
System.out.println(
"Anonymous class B name: " +
getClass().getName());
}//end instance initializer

Listing 5

An argument to the add method

Note that the definition of this anonymous class appears as an argument to the add method for the frame.  Thus, the anonymous object instantiated from the anonymous class is added as the second (middle) button in Figure 2.

Extends the Button class

Note also that this form of anonymous class implicitly extends the Button class.  Once again, this makes it possible to override the processMouseEvent method belonging to the Button class.

An instance initializer

As I mentioned earlier in this lesson, it is not possible to define a constructor for an anonymous class.  However, it is possible to define an instance initializer.  This class defines an instance initializer, which
  • Enables mouse events on an anonymous object instantiated from the anonymous class
  • Gets and displays the name of the class file that represents the anonymous class
The screen output

Therefore, the instantiation of this anonymous object causes the text shown in Figure 6 to appear on the screen.  About all you can tell by looking at this class name is that it is the name of a file that represents an anonymous class.

Anonymous class B name: GUI$1
Figure 6

Overridden processMouseEvent method

The remaining code in the anonymous class definition is shown in Listing 6.

        public void processMouseEvent(
MouseEvent e){
if (e.getID() ==
MouseEvent.MOUSE_CLICKED){
System.out.println(
"buttonB clicked");
}//end if

//Required of overridden
// processMouseEvent method.
super.processMouseEvent(e);
}//end processMouseEvent
}//end class definition
);//end add method call

Listing 6

Basically, the remaining code consists of an overridden processMouseEvent method, and the curly braces, parentheses, and semicolon necessary to complete the expression and the statement.

Same code as before

The code in this overridden processMouseEvent method is essentially the same as that shown for the local class in Listing 4, except that it produces a different message on the screen when the user clicks the button.

Clicking the middle button in Figure 2 produces the screen output shown by the sixth line in Figure 4.

Implementing a listener interface

Now I'm going to switch from low-level event handling to source-listener event handling.  With this event handling model
  • A listener object is instantiated from a class that implements a specific listener interface.  In this case, that interface will be the MouseListener interface.
  • The listener object is registered on an object that knows how to fire events of a type that is associated with the listener interface.  In this case, that will be events of type MouseEvent.
  • When the source object fires an event of the specified type, one of the methods belonging to the registered listener object will be invoked to handle the event.  The different methods belonging to the listener object are declared in the implemented listener interface.
Instantiating and registering a MouseListener object

The code to accomplish this begins in Listing 7.  Listing 7 begins by instantiating a new Button object.
(Note that with this event model, it is not necessary to extend the Button class, because it is not necessary to override methods belonging to the Button object.)
After instantiating a new Button object, the code in Listing 7 invokes the addMouseListener method to register a MouseListener object on that button.  The argument to the addMouseListener method must be a reference to an object instantiated from a class that implements the MouseListener interface.

    Button buttonC = new Button("C");

buttonC.addMouseListener(new MouseListener()
{//begin class definition
//Instance initializer
{System.out.println(
"Anonymous class C name: " +
getClass().getName());}

Listing 7

Instantiate the listener object

In this case, that listener object is created by writing an expression to instantiate an anonymous object from an anonymous class and placing that expression as an argument to the addMouseListener method.

Implement the MouseListener interface

The definition of the anonymous class in this example uses the syntax that implements an interface.

An instance initializer

As before, an instance initializer is used to get and display the name of the class file that represents the anonymous class.  Thus, when the new anonymous object of the anonymous class is instantiated, the text shown in Figure 7 appears on the screen.  Note the similarity of this class file name to that shown earlier in Figure 6.  The names of the two class files differ only with respect to a number that is provided by the compiler to guarantee that each class file name is unique.

Anonymous class C name: GUI$2
Figure 7

Implementing the interface

Whenever a class implements an interface, it must provide a concrete definition for each of the methods declared in the interface, even if some of those methods are empty.

Continuing with the definition of the anonymous class, Listing 8 provides definitions for all five of the methods declared in the MouseListener interface.  Four of those methods are defined as empty methods.

        public void mouseClicked(MouseEvent e){
System.out.println("buttonC clicked");
}//end mouseClicked

//All interface methods must be defined
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}

}//end class definition
);//end addMouseListener call

add(buttonC);//add button to frame

Listing 8

Separation of event subcategories

One of the major differences between the low-level event model discussed earlier and the source-listener model being discussed here has to do with where the separation between the different subcategories (mouseClicked, mousePressed, mouseReleased, etc.) of a given event type is accomplished

In the low-level model, the separation must be accomplished by code in the overridden event handler method, such as with the if statement in the processMouseEvent method defined in Listing 6.

In the source-listener model, the separation is accomplished before the event handler method is invoked, and a specific event handler method, such as the mouseClicked method is invoked on the listener object.

When the button fires a mouse event ...

In this case, whenever the button fires a MouseEvent of the mouseClicked subcategory, the mouseClicked method defined in Listing 8 will be invoked, causing the seventh line of text in Figure 4 to appear on the screen.

Whenever the button fires a MouseEvent of one of the other subcategories, one of the empty methods defined in Listing 8 will be invoked.  This method will return immediately, doing nothing but wasting a little computer time.
(In case you are wondering what happened to the mouseMoved and mouseDragged methods, they are defined in the MouseMotionListener interface instead of the MouseListener interface.)
Add the button to the frame

Finally, the last statement in Listing 8 adds the new button to the frame as the rightmost button in Figure 2.

A disclaimer

I wrote this code the way that I did in Listing 8 to illustrate an anonymous class that implements an interface.  In real life, I would probably cause the anonymous class to extend the MouseAdapter class and override the mouseClicked method instead of implementing the MouseListener interface.  That would eliminate the requirement for me to define the four empty methods in Listing 8.

Extending the WindowAdapter class

The above disclaimer provides a perfect lead-in to the definition of the anonymous class shown in Listing 9.

    addWindowListener(new WindowAdapter()
{//begin class definition
//Instance initializer
{System.out.println(
"Anonymous window listener class " +
"name: " + getClass().getName());}

public void windowClosing(WindowEvent e){
System.out.println(
"Close button clicked");
System.exit(0);
}//end windowClosing
}//end class definition
);//end addWindowListener

setVisible(true);

}//end constructor
}//end GUI class

Listing 9

Registering a WindowListener on the frame

The code in Listing 9 instantiates an anonymous object of an anonymous class, which extends the WindowAdapter class.  That anonymous object is registered as a WindowListener on the frame by passing the object's reference to the addWindowListener method belonging to the frame.
(The addWindowListener method requires an incoming parameter of type WindowListener.  This is satisfied by the fact that the WindowAdapter class implements the WindowListener interface.  Thus, an object instantiated from a class that extends WindowAdapter can also be treated as type WindowListener.)
The screen output

This anonymous class definition uses an instance initializer to get and display the name of the class that represents the anonymous class.  Thus, when the anonymous object of the anonymous class is instantiated, the text shown in Figure 8 appears on the screen.

Anonymous window listener class name: GUI$3
Figure 8

Class file names

In an earlier lesson explaining member classes, I told you that it is possible to examine the names of the class files that represent the member classes and to determine quite a lot about the structure of the program in terms of which classes are members of which other classes.  However, in the case of local classes and anonymous classes, about all that you can determine from the name of the class file is that the file either represents a local class or represents an anonymous class (see the summary of class named in Figure 3).

The windowClosing method

The code in Listing 9 overrides the windowClosing method inherited from the WindowAdapter class.

Clicking the close button with the X in the upper right hand corner of Figure 2 causes the windowClosing method to be invoked on any WindowListener objects that have been registered on the frame.  In this case, the overridden windowClosing method in Listing 9 cases the last line of text in Figure 4 to be displayed on the screen.

Following that, the overridden windowClosing method invokes the System.exit method to terminate the program.

The remaining code

The remaining code in Listing 9
  • Causes the frame to become visible
  • Signals the end of the constructor
  • Signals the end of the GUI class
The GUI remains on the screen until terminated

Once the constructor is executed, the GUI simply remains on the screen waiting for someone to click one of the buttons or to click the close button in the upper right corner of the frame.  When these buttons are clicked, the event handlers are invoked, causing text such as that shown in Figure 9 to appear on the screen.

buttonA clicked
buttonB clicked
buttonC clicked
Close button clicked
Figure 9

Simple event handlers

In this demo program, the event handlers simply display messages on the screen, and in the case of the close button, terminate the program.  In a real world program, the behavior of the event handlers would likely be much more substantive, but the overall skeleton of the program need not be any different from that illustrated here.

Run the Program

At this point, you may find it useful to compile and run the program shown in Listing 10 near the end of the lesson.

Summary

In addition to a number of other items, a class definition can contain:
  • Member classes
  • Local classes
  • Anonymous classes
  • Nested top-level classes and interfaces
Member classes and local classes were explained in previous lessons.  This lesson explains anonymous classes.  The next lesson will explain nested top-level classes and interfaces.

Although there are some differences, an anonymous class is very similar to a local class without a name.

Instead of defining a local class and then instantiating it, you can often use an anonymous class to combine these two steps.

An anonymous class is defined by a Java expression, not a statement.  Therefore, an anonymous class definition can be included within a larger overall Java expression.

Anonymous class definitions are often included as arguments to method calls, or as the right operand to assignment operators.

An object of an anonymous class must be internally linked to an object of the enclosing class.

There is no such thing as an anonymous interface, a local interface, or a member interface.

An anonymous class can often be defined very close to its point of use.  Once you become accustomed to the somewhat cryptic syntax used with anonymous classes, this can lead to improved code readability.

Probably the most important benefit of anonymous classes has to do with accessing the members of enclosing classes.  As with member classes and local classes, methods of an anonymous class have direct access to all the members of the enclosing classes, including private members.  Thus the use of anonymous classes can sometimes eliminate the requirement to connect objects together via constructor parameters.

In addition, objects of anonymous classes have access to final local variables that are declared within the scope of the anonymous class.

An anonymous class can be particularly useful in those cases where

  • There is no reason for an object of the anonymous class to exist in the absence of an object of the enclosing class.
  • There is no reason for an object of the anonymous class to exist outside a method of the enclosing class.
  • Methods of the object of the anonymous class need access to members of the object of the enclosing class.
  • Methods of the object of the anonymous class need access to final local variables and method parameters belonging to the method in which the anonymous class is defined.
  • Only one instance of the anonymous class is needed.
  • There is no need for the class to have a name that is accessible elsewhere in the program.
An anonymous class cannot define a constructor.  However, it can define an instance initializer.  Any arguments that you specify between the parentheses following the superclass name in an anonymous class definition are implicitly passed to the superclass constructor.

Only one instance of an anonymous class can be created.

As with member classes and local classes, anonymous classes cannot contain static members.

As with local variables and local classes, anonymous classes cannot be declared public, protected, private, or static.

What's Next?

The next lesson in this series will explain top-level nested classes.

Complete Program Listing

A complete listing of the program discussed in this lesson is show in Listing 10 below.

/*File InnerClasses08.java
Copyright 2003 R.G.Baldwin

This program is designed to illustrate the use
of local classes, and anonymous classes. It
illustrates three different implementations of
anonymous classes. It also illustrates the use
of instance initializers as an alternative to
constructors.

Illustrates use of local class to instantiate
object to handle mouse clicked event with
low-level event handling. This class uses
constructor to enable mouse events on a new
extended Button class. Also uses constructor
to display the class file name.

Illustrates use of anonymous class to instantiate
object to handle mouse clicked event with
low-level event handling. This class uses an
instance initializer to enable mouse events on a
new extended Button class. Also uses instance
initializer to display name of class file.

Illustrates use of anonymous class, which
implements MouseListener interface, to
instantiate object to handle mouse clicked event
using source-listener event model. Uses instance
initializer to display name of class file.

Illustrates use of anonymous class, which extends
WindowAdapter class, to instantiate object to
handle window events fired by the close button in
the upper-right corner of a Frame object, using
source-listener event model. Uses instance
initializer to display name of class file.

This program produces the following class files
when compiled:

GUI$1$BaldButton.class
GUI$1.class
GUI$2.class
GUI$3.class
GUI.class
InnerClasses08.class

The program produces the following output when
the program is started, each button is clicked
once in succession, and then the close button
in the upper-right corner of the Frame is
clicked:

Local class name: GUI$1$BaldButton
Anonymous class B name: GUI$1
Anonymous class C name: GUI$2
Anonymous window listener class name: GUI$3
buttonA clicked
buttonB clicked
buttonC clicked
Close button clicked

Tested using JDK 1.4.1 under Win
************************************************/

import java.awt.*;
import java.awt.event.*;

public class InnerClasses08 {
public static void main(String[] args){
new GUI();
}//end main
}//end class InnerClasses08
//=============================================//

class GUI extends Frame{

public GUI(){//constructor
setLayout(new FlowLayout());
setSize(250,75);
setTitle("Copyright 2003 R.G.Baldwin");

//Local class w/mouse events enabled. The new
// class extends Button, and uses low-level
// event handling to handle mouse clicked
// events on the button.
class BaldButton extends Button{
BaldButton(String text){//constructor
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
setLabel(text);
//Display the name of the class file
System.out.println("Local class name: " +
getClass().getName());
}//end constructor

//This is the event handling method.
public void processMouseEvent(
MouseEvent e){
if (e.getID() ==
MouseEvent.MOUSE_CLICKED){
System.out.println("buttonA clicked");
}//end if
//The following is required of overridden
// processMouseEvent method.
super.processMouseEvent(e);
}//end processMouseEvent
}//end class BaldButton

//Add button to Frame
add(new BaldButton("A"));


//This code defines an anonymous Inner Class
// w/mouse events enabled. The new class
// extends Button. This class uses low-level
// event handling to handle mouse clicked
// events on the button. This is an
// anonymous alternative to the local class
// defined above.
add(new Button("B")
{//Begin class definition
{//Instance initializer
enableEvents(
AWTEvent.MOUSE_EVENT_MASK);
System.out.println(
"Anonymous class B name: " +
getClass().getName());
}//end instance initializer

//Override the inherited
// processMouseEvent method.
public void processMouseEvent(
MouseEvent e){
if (e.getID() ==
MouseEvent.MOUSE_CLICKED){
System.out.println(
"buttonB clicked");
}//end if
//Required of overridden
// processMouseEvent method.
super.processMouseEvent(e);
}//end processMouseEvent
}//end class definition
);//end add method call


Button buttonC = new Button("C");
//Anonymous inner class that implements
// MouseListener interface
buttonC.addMouseListener(new MouseListener()
{//begin class definition
//Instance initializer
{System.out.println(
"Anonymous class C name: " +
getClass().getName());}

public void mouseClicked(MouseEvent e){
System.out.println("buttonC clicked");
}//end mouseClicked

//All interface methods must be defined
public void mousePressed(MouseEvent e){}
public void mouseReleased(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}

}//end class definition
);//end addMouseListener call

add(buttonC);//add button to frame


//Use an anonymous class to register a window
// listener on the Frame. This class extends
// WindowAdapter
addWindowListener(new WindowAdapter()
{//begin class definition
//Instance initializer
{System.out.println(
"Anonymous window listener class " +
"name: " + getClass().getName());}

public void windowClosing(WindowEvent e){
System.out.println(
"Close button clicked");
System.exit(0);
}//end windowClosing
}//end class definition
);//end addWindowListener

setVisible(true);

}//end constructor

}//end GUI class
//=============================================//

Listing 10

Copyright 2003, Richard G. Baldwin.  Reproduction in whole or in part in any form or medium without express written permission from Richard Baldwin is prohibited.

About the author

Richard Baldwin is a college professor (at Austin Community College in Austin, Texas) and private consultant whose primary focus is a combination of Java, C#, and XML. In addition to the many platform and/or language independent benefits of Java and C# applications, he believes that a combination of Java, C#, and XML will become the primary driving force in the delivery of structured information on the Web.

Richard has participated in numerous consulting projects, and he frequently provides onsite training at the high-tech companies located in and around Austin, Texas.  He is the author of Baldwin's Programming Tutorials, which has gained a worldwide following among experienced and aspiring programmers. He has also published articles in JavaPro magazine.

Richard holds an MSEE degree from Southern Methodist University and has many years of experience in the application of computer technology to real-world problems.

baldwin@DickBaldwin.com

-end-
 






Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel