This week's giveaway is in the EJB and other Java EE Technologies forum. We're giving away four copies of EJB 3 in Action and have Debu Panda, Reza Rahman, Ryan Cuprak, and Michael Remijan on-line! See this thread for details.
I often find that within a particular class I need to implement several listeners. This is generally in classes that need to present a GUI or work tightly with the presentation of one. I've been trying to find an elegant solution to dealing with all these listeners and wondering what most people consider a good solution. So what do you use?
Anonymous classes? I did this in a couple classes, but I wonder about the implications of having so many anonymous classes floating around. I also wonder about how hard it will be for someone to undertand my code in the future, that anonymous class isn't necessarily easy to find and it's affects may not be easy to trace. Furthermore, if objects outside the scope of where the anonymous class is created could have used that same functionality it has to be replicated elsewhere in the code needlessly.
Implement it as part of the class? I really don't like this. First, usually my use of listeners is an implementation detail. Implementing the listener effectively exposes this detail. For example, a generic Query window that displays a JTable might have "Accept" and "Cancel" buttons requiring an ActionListener. The use of this ActionListener is an implementation detail, so Query implements ActionListener that is exposed needlessly. Also, this tends to clutter up my code with all kinds of methods relating to listeners, many of which are unused.
Inner class? My terminology may be wrong, I'm talking about a class within another class. Such as:
I like this in the sense that I need only instantiate one listener and all the code for those listeners is in one place, easy to find. I don't like it in the sense that the ActionListener may be needed for something completely irrelevent to the MouseListener. Yet they're in the same place. Worse yet, it requires additional code to check for the source that may not have been necessary otherwise and can clutter things up and reduce readability and understanding.
Inner class for each kind of listener? Take the above, but break them up so that the there's a QueryMouseListener and QueryActionListener. But then there's a whole bunch of inner classes cluttering things up.
So I'm curious as to what a good programming practice is for handling this. I'll give you an example that relates to something I'm working on. We'll call it a Query window and it's sole purpose will be to display JTable that a user can select something from. Seems pretty easy, but it's going to require a ton of listeners.
We need an ActionListener because we want to have an Accept button and a Cancel button, add that to the buttons as an anonymous class. Double clicking on a row should select it and exit as well, so that requires a MouseListener, which can also be an anonymous class. Hitting enter should do it as well and hitting a digit key should select the row (i.e. hit 5 and it selects the 5 row being displayed), that requires a KeyListener. A fun part of this query is going to be that it retrieves from a database, so it needs to fetch more results as the user nears the end of what it's showing already, so that requires an AdjustmentListener on the vertical scrollbar of the scroll pane the table is in. We also need to disconnect from the database when it's closed, so that needs a WindowListener in case the user closes the window. Oh, the row size isn't fixed either because it contains descriptions that are wrapped in a JTextArea and thus each row could be a different size, so to display our x rows at all times we need to change the size of the dialog depending on which rows are being shown, which also requires use of the AdjustmentListener to know when the user has scrolled so we can calculate what rows are being shown and their sizes to know how big the dialog should be.
You very quickly end up with a whole slew of listeners. Some are unrelated, some aren't. Some have common code and should be used with multiple objects that aren't necessarily instantiated in the same place. What can you do with it to ensure it's not confusing or hidden from future programmers and isn't exposing implementation details that it shouldn't? [ October 24, 2005: Message edited by: Ken Blair ]
I prefer anonymous classes myself. To me they say something like "When the user clicks button X, do this" or "When the user selects a tree node, do this". I find it perfectly clear (although if you're concerned about future maintainers you could add a comment line explicitly saying what's happening). And if you need two anonymous classes with the same functionality (e.g. you have two ways for the user to do one thing) then they can just call a method in their containing class.
Sure, if you have a lot of listeners then you have a lot of anonymous classes. I don't find that any worse than having a lot of methods in a class, because usually an anonymous class is just a method with a little bit more complex way of being called.
I completely agree with you that having the class itself implement an interface isn't a good thing, for basically the reasons you describe. Your Query window HAS A key listener, so composition is appropriate (and hence most likely an inner class of some kind). It is not true that it IS A key listener, so it should not implement the KeyListener interface.
Generally I use anonymous inner classes. When Its an abstract base class I often use anonymous inner class to hear the events then forward the event onto a protected method of the same name on the main class.
Yes, loads of anonymous inner classes. I see no problem with that.
Joined: Jul 15, 2003
Oh, I hadn't thought of using private/protected methods in the base class that an anonymous inner class can forward to. That would be an elegant solution to most if not all of the issues I had with it.
My question would be how do you handle the unregistering of listeners? I worry about lots of anonymous listeners manifesting in memory leaks because they're added and never removed.