Generating Event Listeners Dynamically
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
As you can see, the listener in this case simply dispatches the
The ActionListener ProblemFortunately, 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 WorkTo 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 ActionListenerIn 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 :
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
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 ClassesSo 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 Here's an example (We're using the a convenient version of the
Here we've created an ActionListener whose actionPerformed()
method calls 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:
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. AcknowledgementThe 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. | |||||||||||||||
|
| ||||||||||||