Tips for Slimming Down Handler Code

Inner classes are intended for event handlers. The inner classes allow you to put the event-handling class and method right next to where you declare the control or register the callback listener. Anonymous classes are a refinement of inner classes, allowing you to combine the definition of the class with the instance allocation. The following example shows the code rewritten using an anonymous class:

import javax.swing.*;
import java.awt.event.*;
public class CloseDemo2 {

    public static void main(String[] args) {
          JFrame jframe = new JFrame("Example");
          jframe.setSize(400,100);
          jframe.setVisible(true);

          jframe.addWindowListener( new WindowListener() { // anon. class
               public void windowClosing(WindowEvent e) {System.exit(0);}
               public void windowClosed(WindowEvent e) { }
               public void windowOpened(WindowEvent e) { }
               public void windowIconified(WindowEvent e) { }
               public void windowDeiconified(WindowEvent e) { }
               public void windowActivated(WindowEvent e) { }
               public void windowDeactivated(WindowEvent e) { }
          } ); // end of anonymous class.
    }
}

Try to compile and run this code example. Your CloseDemo2.java file generates class files called CloseDemo2.class and CloseDemo2$1.class. The second item represents the anonymous WindowListener inner class.

You should use inner classes and anonymous classes only where the event handler is just a few lines long. If the event handler is more than a screenful of text, it should be in a named top-level class. We have to admit, however, that the notational convenience for smaller cases is considerable—just don't get carried away.

There are two further refinements: make your top-level class implement the appropriate listener interface and use an adapter class. These techniques further reduce the amount of “housekeeping code” you need to write. You'll see them in other programmers' code. I'll present them here so you can recognize the pattern.

Making a top-level class into a listener

You don't have to declare a separate class to implement the listener interface. You can make any of your existing classes do the work by adding the handler methods and the “implements somethingListener” clause.

The following code demonstrates this idea:

import javax.swing.*;
import java.awt.event.*;
public class CloseDemo3 implements WindowListener {

    public static void main(String[] args) {
        JFrame jframe = new JFrame("Example");
        jframe.setSize(400,100);
        jframe.setVisible(true);

        jframe.addWindowListener( new CloseDemo3() );
    }

    public void windowClosing(WindowEvent e) {System.exit(0);}

    public void windowClosed(WindowEvent e) { }
    public void windowOpened(WindowEvent e) { }
    public void windowIconified(WindowEvent e) { }
    public void windowDeiconified(WindowEvent e) { }
    public void windowActivated(WindowEvent e) { }
    public void windowDeactivated(WindowEvent e) { }
}

In this example, you make the demo class itself implement WindowListener. The body of the class provides all the methods that WindowListener demands. When you want to add the WindowListener, you just instantiate an object of the demo class and the work is done in the main method (which is static). If you were making that call in an instance method, the line would be even simpler:

jframe.addWindowListener( this );

It's a handy technique, but you're not done yet. You can make the code even shorter, as the next section explains.

Using a listener adapter class

Even though you were interested only in the windowClosing event, you had to supply null bodies for all the methods in the WindowListener interface. To make things a little more convenient, a concept called adapter classes can be used. An adapter is one specific example of a design pattern. An adapter is the design pattern that converts the API of some class into a different, more convenient API.

In Java AWT event handling, you might want to implement only one or two functions to handle the one or two events of interest for some of the Listener interfaces (such as WindowListener). The SomethingListener interface may specify half a dozen methods. The language rules are such that you must implement all the functions in an interface even if you just give them empty bodies, as shown earlier in the WindowListener. The package java.awt.event provides adapters that help by allowing you to override as few methods as you like. The adapters include the following:

  • ComponentAdapter

  • MouseMotionAdapter

  • WindowAdapter

  • ContainerAdapter

  • MouseAdapter

  • FocusAdapter

  • KeyAdapter

These adapters are classes that provide empty bodies for all the methods in the corresponding SomethingListener interface. Following is an example of the WindowAdapter.java adapter:

public abstract class WindowAdapter implements WindowListener {
     public void windowOpened(WindowEvent e) { }
     public void windowClosing(WindowEvent e) { }
     public void windowClosed(WindowEvent e) { }
     public void windowIconified(WindowEvent e) { }
     public void windowDeiconified(WindowEvent e) { }
     public void windowActivated(WindowEvent e) { }
     public void windowDeactivated(WindowEvent e) {}
}

If you can declare your event handler as a subclass of one of these adapters, you can provide only the one or two methods you want, instead of implementing all the methods in the interface. This way, you let inheritance do the work. Another way to do this is to have one Adapter class implement all the Listener classes with null methods for all of them. This way you don't have to remember all the individual adapter names. That's the way I would have done it, which is probably why I'm not on the Swing design team.

Since Java classes have only one parent, you can't use this technique if you already inherit from some other class (although you can always create a new class just to make it a subclass of some adapter).

The following code is an example of how the WindowAdapter class is used when you are interested only in the windowClosing event:

import javax.swing.*;
import java.awt.event.*;
public class CloseDemo4 extends WindowAdapter {

     public static void main(String[] args) {
          JFrame jframe = new JFrame("Example");
          jframe.setSize(400,100);
          jframe.setVisible(true);

          jframe.addWindowListener( new CloseDemo4() );
     }

     public void windowClosing(WindowEvent e) {System.exit(0);}
}

The code is much shorter and easier to understand. What could be simpler than an adapter? Well, it turns out that there is a major pitfall with adapter classes, and it's one of those awful problems that leaves you swearing at the keyboard the first time you encounter it. You'll know to check for it after that, but the first time is a little frustrating.

We'll use KeyAdapter to show the problem, rather than WindowListener. The KeyListener is an interface used to send keyboard events. A keyboard event is generated when a key is pressed, released, and typed (pressed and released). There are KeyListener methods for all three of these. For this example, let's say you are going to use an adapter because you're interested only in the event that comes to keyPressed() and none of the other key-related events. Let's create an anonymous class for the KeyAdapter.

When you create an inner class for an adapter class, you simply supply the one or two methods that you wish to override. This concept is shown in the following code:

new KeyAdapter() {
     public void keyPressed(java.awt.event.KeyEvent e)
     { System.out.println("got "+e.getKeyChar()); }
} // end anon class

You may, however, make a small spelling or letter case error in supplying your method. This error is shown in the following code:

new KeyAdapter() {
     public void KeyPressed(java.awt.event.KeyEvent e)
     // Notice capital "K" in "KeyPressed" WRONG!
     { System.out.println("got "+e.getKeyChar()); }
} // end anon class

A spelling mistake like this means that your method will not override the intended method in the adapter class. Instead, you have added a new method that never gets invoked! The empty body of the correctly spelled method in the adapter class will be invoked instead, and it will do nothing. If your event handler seems to do nothing and you used an adapter, you should first check that the method name and signature exactly match something in the adapter class.

As I mentioned at the start of the chapter, learning how to handle events is like clearing your plate of vegetables before being allowed to have dessert. You've now eaten enough vegetables, so I'll wrap up this chapter with a summary.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.111.179