Generating Event Listeners 
              Dynamically
             
             
            
             
               Swing 
                uses ActionListeners to add behavior to nearly all of its GUI 
                controls -- and the anonymous classes that JavaBeans use to implement 
                event listeners have been accused in some quarters of consuming 
                more class file space than they should. This article shows how 
                Swing can help you get around that problem. With the help of several 
                code samples that you can download and experiment with, it offers 
                some suggestions that may be of interest to developers of IDEs 
                and other kinds of applications that generate listeners for Swing 
                or AWT. 
             
             
            By Hans Muller 
             One charge that gets leveled against the Java Beans event model 
              from time to time is that the anonymous classes used to implement 
              event listeners consume more class file space than they should. 
              Event listener classes implement one of the many interfaces derived 
              from java.util.EventListener. For example, to handle 
              keyboard events, you can add an implementation of java.awt.event.KeyListener 
              to a component's "key" listener list: 
             
KeyListener myKeyListener = new KeyListener() {
    public void keyTyped(KeyEvent e) { handleKeyEvent(e); }
    public void keyPressed(KeyEvent e) { handleKeyEvent(e); }
    public void keyReleased(KeyEvent e) { handleKeyEvent(e); }
};
myComponent.addKeyListener(myKeyListener);
            As you can see, the listener in this case simply dispatches the 
              KeyEvent to another method, called handleKeyEvent(). 
              Using a listener as a sort of "trampoline" to shuttle events from 
              a listener method to some event-handling method in the outer class 
              is a common idiom. In fact, some GUI builders generate boilerplate 
              listener implementations that do exactly that. Today's JavaTM 
              compilers produce a class file like "myListener" 
              for each anonymous inner class. Usually the class file gets a funny 
              name such as "MyClass$3". 
             
             
            The ActionListener Problem
            Fortunately, most applications don't have many KeyListeners. However, 
              Swing applications tend to be teeming with ActionListeners. ActionListeners 
              are used to add behavior to nearly all of Swing's GUI controls -- 
              notably buttons, combo boxes, and menu items. 
             This can be a serious concern in applets; applets should download 
              as quickly as possible, and yet each inner class file used in an 
              applet contains a block of potentially redundant information (beyond 
              the few byte codes that represent the applet's listener method implementations 
              themselves). If these listener classes aren't just trampolines but 
              actually do significant work, then the cost of the extra class files 
              probably isn't worth worrying about. However, when an IDE or other 
              system unconditionally generates this kind of thing, it's worth 
              considering the remedies presented in this article.
              
            How the Remedies Work
            To illustrate how these remedies work, this article presents several 
              source files, titled: 
             
            You'll get a chance to see how all these parts fit together later 
              in the article. 
             
            Using Reflection 
              to Implement a Generic ActionListener
             In the future, it's quite possible that separate class files will 
              not be generated for each inner class, or that inner class files 
              will be relatively small. However, in the short term, the problem 
              does exist, so the solutions outlined in this article should be 
              of interest to anyone developing an IDE or other application that 
              generates listeners for Swing or AWT. 
             Many developers have observed that by using reflection to dispatch 
              from a listener to some other handling method, one can get by with 
              just one listener implementation per listener type. The following 
              example (with exception handling omitted) shows how easy it is to 
              create a generic ActionListener that invokes a method on some target 
              object : 
             
class GenericActionListener implements ActionListener {
    private final Object target;
    private final Method targetMethod;
    GenericActionListener(Object target, Method targetMethod) {
	this.target = target;
	this.targetMethod = targetMethod;
    }
    public void actionPerformed(ActionEvent e) {
	targetMethod.invoke(target, new Object[]{e});
    }
}
             The one class created in this example -- GenericActionListener 
              -- can be used for all of the ActionListeners in an application. 
              To use GenericActionListener, you must look up the method object 
              that the listener will invoke. For example, to create an ActionListener 
              that calls an ActionEvent method called buttonAction, 
              you could write a method like this (exception handling ommitted): 
             
Method m = getClass().getMethod("myAction", new Class[]{ActionEvent.class});
myButton.addActionListener(new GenericActionListener(this, m));
             This kind of solution works well enough for small GUI applications. 
              In general, however, it requires the creation of a similar boilerplate 
              class for each listener type, and that can get oppressive. As it 
              turns out, it's possible to generate the bytecodes that define simple 
              classes like GenericActionListener at runtime -- and they can be 
              generated for any listener interface. The fact that such a class 
              can be generated at runtime is of particular importance to developers 
              of IDEs and other tools because it enables event links to be created 
              between components (classes) that are loaded at run-time -- that 
              is, those whose listener interfaces aren't known until runtime. 
              
            GenericListener: Dynamically Generating 
              Listener Classes
             So far, we've defined a new utility class called GenericActionListener 
              that can be used to generate a listener interface implemention in 
              which one listener method dispatches to a method on some target 
              object. To see how this new class works, you can download and examine 
              the source files supplied with this article: 
             
            The GenericListener.create() method code generates 
              a "trampoline" listener class, if one doesn't exist already, and 
              then loads the new class with a custom class loader. The trampoline 
              class is configured to apply a method that you specify to a target 
              object. If you take the black-box view of GenericListener.create(), 
              then you provide a listener method, a target object, and a target 
              object method, and the create() method returns a listener 
              object where the specified listener method calls targetObject.targetMethod(event). 
             
            Here's an example (We're using the a convenient version of the 
              create() method that allows us to specify string method 
              names): 
             
ActionListener l = (ActionListener)(GenericListener.create(
    ActionListener.class, 
    "actionPerformed", 
    myTargetObject, 
    "myTargetObjectMethod"));
myButton.addActionListener(l);
             Here we've created an ActionListener whose actionPerformed() 
              method calls myTargetObject.myTargetObjectMethod(anActionEvent). 
              If the ActionListener interface doesn't contain a method named actionPerformed(), 
              or if the target object doesn't contain a method named myTargetObjectMethod() 
              that matches the signature of actionPerformed(), 
              a runtime exception is thrown. Naturally, the value returned by 
              the static GenericListener.create() method has to be 
              cast to the correct listener type, since it can return any type 
              of listener. 
              To see a complete program that creates a MouseListener and an 
              ActionListener, see the Demo.java 
              example. 
              This approach has two important advantages of this approach over 
              the strategy of managing a set of trampoline classes that was described 
              in the preceding section: 
             
              -  The internal listener "trampoline" classes are generated automatically 
                and at runtime. They don't affect download time, and they have 
                no more effect on the applications working set than a minimal 
                set of similar hand-written classes would.
 
                 
               -  IDEs and other tools that load classes or components dynamically 
                can use GenericListener to generate event bindings for listener 
                interfaces (and event types) that aren't known until run-time. 
            
  
             
              WARNING: 
              The GenericListener implementation takes about 2,000 lines of pure 
              Java programming language code, and most developers will probably 
              find it moderately difficult to read -- although developers who 
              have spent quality time with Java compiler implementations, and 
              who are familiar with the VM (virtual machine) and the class loader, 
              shouldn't have too much trouble understanding it. 
              A variety of simple extensions can be applied to the GenericListener 
              class itself; for example, it would be easy to add support for having 
              all listener methods dispatch to the target method. However, our 
              advice for the rest of the classes is: If you don't like Java compilers, 
              don't look. 
               
              
            Acknowledgement
             The GenericListener class is really just a thin veneer over a 
              more general-purpose infrastructure for generating arbitrary interface 
              implementations built by John Rose. John is a member of the Sun/Java 
              Software Java compiler team. Without his help, we wouldn't have 
              dared take this work farther than GenericActionListener. 
               
              
            Hans Muller
             
              
           |