Java Programming   Java Programming
 |Sofia Home | Content Gallery |
Home
Syllabus
Schedule
Lessons
Assignments
Resources

Lesson 12.5 Event Adapters & Inner Classes

In this section, you'll look at a different way of handling events: through the use of inner classes and the Java 1.1 Event Adapter classes. If inner classes seem familiar, that's because you met them earlier in Lesson 3, The Button Class.

Inner classes are nested classes that you define "inside" other classes. When you compile your program, Java combines the name of your outer [containing] class with the name of your inner class to create the actual "real" name of the class.

Here's an example:
public class Outer 
{
  private int a = 10;

  class Inner
  {
    public void changeIt()
    {
      a = 5;
    }
  }
}

The outer class, here imaginatively named Outer contains only a single private int field named a, as well as the definition for an inner class named Inner. There are two important things to note about this example:

  • The method changeIt(), defined in the class Inner, has access to the private data field a, while a subclass of the Outer class would not. In this sense, inner classes act more like body parts than like relatives. [Earlier versions of the Microsoft JVM had problems with this requirement. The versions shipped with the 4X series of IE seem to handle it OK.]
  • The example program creates no Inner object, it only contains a definition. To actually use the inner class you must also create an Inner object.

When you compile this, as you can see here, you don't end up with files named Outer.class and Inner.class. Instead, you have Outer.class and Outer$Inner.class.

Files created when compiling a program containing inner classes.

The main reason for using inner classes is to respond to events. By creating a separate inner class for each event you want to handle, your code is more modular.

Let's look at an example.

Back to Top

A Simple ActionListener

Let's create a class that has two Buttons, and use inner classes to hook up their events. This is an intentionally simplified example.
Step 1: The Framework
The first step is to create the overall framework:
  1. Create an applet with a Label and two Buttons.
  2. Put "A Simple Action Applet" in the Label.
  3. Name your Buttons show and hide.
  4. Add all three items to the surface of your applet.

Your code should look like this:

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

public class SimpleAction extends Applet
{
  private Label  lbl  = new Label("Simple Action Applet");
  private Button show = new Button("Show");
  private Button hide = new Button("Hide");

  public void init()
  {
    add(lbl);
    add(show);
    add(hide);
  }
}

Step 2: Inner Classes
Now create the inner class definitions. Make sure that these go inside the SimpleAction class definition, but not inside one of the methods, such as init(). Here are the steps:
  1. Name your classes Shower and Hider.
  2. Have them implement ActionListener.
  3. Each one should contain an actionPerformed() method.
  4. Make the Label visible in one and invisible in the other.

Here's what your code should look like now: 

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

public class SimpleAction extends Applet
{
  private Label  lbl  = new Label("Simple Action Applet");
  private Button show = new Button("Show");
  private Button hide = new Button("Hide");

  public void init()
  {
    add(lbl);
    add(show);
    add(hide);
  } 

class Shower implements ActionListener
{
  public void actionPerformed(ActionEvent ae)
  {
    lbl.setVisible(true);
  }
}
 
class Hider implements ActionListener
{
  public void actionPerformed(ActionEvent ae)
  {
    lbl.setVisible(false);
  }
}
}
Back to Top
Step 3: Objects & Connections
At this point, you have two inner classes but you don't have any Shower or Hider objects. Furthermore, since you don't have any handler objects, your Buttons have nowhere to send their event notifications. 

Let's fix that:

  1. Add two fields, hider and shower, using the appropriate constructor to initialize them.
  2. Connect each Button to the appropriate handler object, by sending it the addActionListener() message.
Your finished code should look like this: [Changes are highlighted]

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

public class SimpleAction extends Applet
{
  private Label  lbl  = new Label("Simple Action Applet");
  private Button show = new Button("Show");
  private Button hide = new Button("Hide");

  private Hider  hider  = new Hider();
  private Shower shower = new Shower();

  public void init()
  {
    add(lbl);
    add(show);
    add(hide);

    hide.addActionListener(hider);
    show.addActionListener(shower);

  }

  class Shower implements ActionListener
  {
    public void actionPerformed(ActionEvent ae)
    {
      lbl.setVisible(true);
    }
  }

  class Hider implements ActionListener
  {
    public void actionPerformed(ActionEvent ae)
    {
      lbl.setVisible(false);
    }
  }
}

Back to Top

Variations on a Class

There are two different variations on this basic inner-class arrangement that are commonly used. It's important that you recognize them, but remember that each of them does the same thing as the SimpleAction class you've just seen; the inner classes are just arranged a little differently.
Variation 1: Anonymous Variables
This one is easy to understand. In SimpleAction, the two variables shower and hider have only a single purpose: to create a Shower object and a Hider object to be passed to the appropriate Button in addActionListener().

Since you really don't need these two fields for any other purpose, you can eliminate the variables entirely, replacing them with anonymous variables like this:

hide.addActionListener(new Hider());
show.addActionListener(new Shower());
Variation 2: Anonymous Classes
This one is a little harder to grasp, because it's easy to get the syntax confused. 

In the SimpleAction applet, the classes Shower and Hider are only used to each create a single object--the object passed to addActionListener(). Since you really don't need the class definition in any other context, Java allows you to create an anonymous class at the same place you create the object of the class: when you call addActionListener().

To understand this syntax, think of the original class definition for the Hider class shown here, with the identifying information stripped out:

class Hider implements ActionListener
{
  public void actionPerformed(ActionEvent ae)
  {
    lbl.setVisible(false);
  }
}

Now, imagine that this definition is used to replace the Hider() constructor in the anonymous variables variation like this:

hide.addActionListener(new Hider/* insert here */());

The result is an anonymous class object that looks like this:

hide.addActionListener(new ActionListener ()
{
  public void actionPerformed(ActionEvent ae)
  {
    lbl.setVisible(false);
  }
});
Back to Top

Should You?

Should you use anonymous classes? Many programmers like them because using anonymous classes makes their code slightly shorter, plus they don't have to think up "dummy" class names. Others feel that it makes it harder to follow the outline of the original program. 

You'll have to decide for yourself. There's no doubt, however, that anonymous classes, unless used in moderation, make a program harder to read and understand.

Introducing Adapters

Lesson 12.1 introduced you to the WindowListener interface, which you used to respond to the windowClosing() message in your GuiApp class. Implementing the WindowListener interface required you to write seven methods. All but one of these methods, however, were dummy methods, which seems kind of a waste.

Instead of writing all seven methods, why not simply create a superclass, that has dummy methods for all seven? Then, instead of writing out the dummy methods over and over, you can just create a subclass, and override only the methods you want.

That's a really good idea. So good, in fact, that the designers of Java have already done this with the adapter classes. An adapter class is simply a class which implements a specific event interface and then provides dummy methods for each of the required methods.

Back to Top

Exploring Adapters

Java provides seven different adapter classes--one for each of the event interfaces that contain multiple methods. 

No adapter classes are provided for interfaces such as ItemListener or ActionListener, which contain only a single method, because there would be no advantage to using the adapter class over the interface.

The seven adapter classes provided by Java include:

  • ComponentAdapter
  • ContainerAdapter
  • FocusAdapter
  • KeyAdapter
  • MouseAdapter
  • MouseMotionAdapter
  • WindowAdapter

Using Adapters

To use an adapter class you follow these steps:
  1. Create an inner class that extends one of the existing adapter classes.
  2. Override the methods of interest in your inner class.
  3. Create an object of your inner class and send event notifications to the new object.


Using the MouseAdapter class

Back to Top

Using WindowAdapter

Let's take a look at the GuiApp class and see how we could simplify it by using the WindowAdapter class instead of WindowListener.

Here's the original code, with the name changed and the necessary changes highlighted:
GuiApp2.java
import java.awt.*;
import java.awt.event.*;

public class GuiApp2 extends Frame
         implements WindowListener
{
  // 1. Working constructor --------------------
  public GuiApp2(String title) 
  { 
    super(title);
    addWindowListener(this); // Window events
    addWindowListener(new Closer());
    setSize(300, 250);       // Default size
    setLayout(new FlowLayout(FlowLayout.CENTER));
  }

  // 2. Default constructor ---------------------
  public GuiApp2()
  {
    this("GuiApp"); 
  }   // 3. Quit the application
  public void quitApp()
  {
    dispose();
    System.exit(0);
  }

  // 4. WindowListener methods -------------------
  public void windowOpened(WindowEvent we) {}
  public void windowClosed(WindowEvent we) {}
  public void windowIconified(WindowEvent we) {}
  public void windowDeiconified(WindowEvent we) {}
  public void windowActivated(WindowEvent we) {}
  public void windowDeactivated(WindowEvent we) {}
  class Closer extends WindowAdapter
  {
    public void windowClosing(WindowEvent we)
    {
      quitApp();
    }
  }
}

The version using WindowAdapter is shorter [or it would be if I hadn't left all the deleted lines in it] and clearer than the version implementing WindowListener because your code isn't cluttered up with all those dummy methods.

Back to Top

Something to Talk About

Here's a challenge! I have some partially completed code for a simple applet that contains a red and green Label. Can you add the necessary inner class [named Switcher] to reverse the colors of the Label when the mouse is moved over it, and switch them back when the mouse is moved off?

  • You'll want to extend the MouseAdapter class, and override the mouseEntered() and mouseExited() methods.
  • Your code should work like the applet shown below:
import java.awt.*;
import java.awt.event.*;
import java.applet.*;

public class Wrapup12e extends Applet
{
  private Label lbl = new Label("Mouse Me");

  public void init()
  {
    lbl.setFont(new Font("Serif", Font.BOLD, 18));
    lbl.setForeground(Color.red);
    lbl.setBackground(Color.green);
    add(lbl);
    lbl.addMouseListener(new Switcher());
  }

  // Write the stuff that goes here
}

Please continue to the next section of this lesson.

 

Back to Top

 

Content Developed by Stephen Gilbert, Licensed under a Creative Commons License
Published by the Sofia Open Content Initiative
© 2004 Foothill-De Anza Community College District &The William and Flora Hewlett Foundation