Java Basics - Commentary
Listener alternatives
Q: What's the best way to handle action (or other) events? Java is plagued by an abundance of ways to handle listener events: anonymous inner classes (several variations), named inner-classes, subclassing AbstractAction, implementing Action, EventHandler, ActionMaps and ActionMapUIResource, reflection, event busses, single listener method for all events, alternate frameworks, ... . You will, unfortunately, see all of these used at one time or another.
A: There is no clear good way, but there are bad ways. That there is no simple best way to implement listeners is a failure of language design / libraries / leadership. Sun Microsystems is (as of this writing) at at last proposing some guidance on this matters in the upcoming JSR 296 - Swing Application Framework. It hasn't been made public yet, but it looks like Actions may be one of the first choices. I'll mention pros and cons of various approaches, and where some are more appropriate than others.
Recommendations. For small programs, I've recommend the following, in this order. When JSR 296 becomes available, it's likely that subclassing AbstractAction will become the top choice.
- Named inner classes - Provides good separation of GUI details from listeners. My first choice in examples.
- Anonymous inner class - OK for small program, but quickly becomes unmaintainable as program size grows.
- AbstractAction subclass - Similar to the named inner class approach. It's a little more work for small programs, but less work for larger programs.
Buttons, menu items, and other components call an action listener method
when clicked (or whatever). This calls the actionPerformed
method in the object that implements the ActionListener
interface. There are several ways to do this, and some of them are bad.
Named inner class listeners - Good for small programs
- Example
- See GUI 6 - DogYears - Listeners.
- Pros
- Easy to understand.
- Easy to access the components in the outer class.
- Very readable with a good name.
- Gives nice structure by textually separation from the rest of the GUI construction.
- Similarity to subclassing AbstractAction makes natural growth path to larger programs.
- Cons
- Bulky. They take more screen space
Anonymous inner class listeners - Small programs only.
Anonymous inner class listeners are commonly used. In some cases they are a good technique, but they also can lead to messy code if you're not careful. They require somewhat obscure syntax, and require using final variables when defined in a method. Look at Commentary - Build window in main() for an example.
NetBeans uses this style in automatically generated code, and makes it simple for the programmer by calling an automatically generated method where the programmer can place code.
AbstractAction (good)
Creating an AbstractAction listener object for each action is a nice way to handle events. An AbstractAction provides the text, icon, enabled/disabled status that will be used by the button or menu item. This isn't usually taught in introductory courses because it really starts to show its value only when the program gets larger. [TODO: needs example]
Separate top-level listener classes
[TODO: show non-private fields or getter/setter methods]
One action listener for all buttons / menu items (bad)
Sometimes you'll see one action listener defined in the GUI, eg, a JFrame subclass that implements ActionListener. It then proceeds with cascading series of else-ifs. The "action command" defaults to the text on a button or menu item, but can be set explicitly, which is necessary if the program is to be internationalized.
In small example programs with only one component which generates action events (true only for very small programs), there's no problem, but as soon as multiple components generate them, you are forced to decode the events yourself, which becomes increasingly complex as the numbers and types of components in your GUI increase.
private class GUIActionListener implements ActionListener { public void actionPerformed(ActionEvent event) { String actionCmd = event.getActionCommand(); if (actionCmd.equals("about")) { showAbout(); } else if (actionCmd.equals("open")) { openFile(); } else if (actionCmd.equals("new")) { createNewInstance(); } else if (actionCmd.equals("quit")) { shutdown(); . . . } return; } }
Historical solution. This was the only option available in early Java, which was where this all got started. I believe it's the style for handling events in the Micro Edition also, but it's not the best for most programs.
Specific objections. There are a couple of reasons for not using one single listener, but let's imagine a case where there are a lot of components, for example, MS Word. You can imagine the many hundreds of buttons, menu item, etc that generate ActionEvents (assuming it was written in Java).
- Doing sequential string comparisons is ugly, even if you don't worry about the inefficiency.
- It's hard to modularize a large GUI if everything goes to one listener.
It's like having all method calls in a class enter at one dispatcher that tested a string and decided with method to call. No one would stand for that kind of nonsense. It's trivial to have a separate listener for each action, and offensive to have everything come to one listener only to be divided into separate calls.
Better is to have a separate listener for each distinct action.