• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Tim Cooke
  • Ron McLeod
  • paul wheaton
  • Jeanne Boyarsky
Sheriffs:
  • Paul Clapham
  • Devaka Cooray
Saloon Keepers:
  • Tim Holloway
  • Roland Mueller
  • Himai Minh
Bartenders:

Event Listeners: Anonymous Inner Class vs. External Class

 
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I use NetBeans as my IDE. When you create a JButton and then want code to handle its ActionEvent, the IDE will write it for you like this:


If I am reading this correctly, it appears to be creating an anonymous inner class that implements the ActionListener interface by providing an actionPerformed method. That method calls btnPrintActionPerformed, which is created as a stub method in the enclosing class. I get to add my code to handle the event (that is, to respond to the button being pushed) in btnPrintActionPerformed.

Now, in their tutorials, Oracle sometimes uses objects of entirely different classes to provide event handlers. For example, at "Introduction to Event Listeners", they offer this fragment:

Here, Eavesdropper is a class all its own, external to MultiListener. Eavesdropper has its own "actionPerformed" method, in which the programmer can provide whatever code is needed to handle the event.

The anonymous inner class has the advantage of direct access to all of the enclosing object's fields and methods. But it requires a method-within-a-method approach (with the actual event handler being called from inside the anonymous inner class's actionPerformed method), and (I am noticing) tends (in my own case, anyway) to lead to large class files with lots and lots of event handlers (one for every menu item, one for every button, and one for everything any other Swing control might generate as an event I need to handle).

The external class has the advantage of keeping the total amount of code in such a class low (instead of dumping it all into the same class that has the objects that generate the events, like NetBeans does), but it does require a certain amount of "has-a" structure so it knows enough about the other objects in my application to do whatever it is that needs doing when (say) a button is pushed. (You can see this in Line 7 of Oracle's fragment, where the Eavesdropper constructor is passed a reference to "bottomTextArea," the control it will modify when the event is handled.) It also requires that I do some manual coding to add the listener, since I can't figure out how to get NetBeans to ask me for listener objects (instead of always creating the anonymous inner class object, as above).

I would be grateful to know others' opinions on which of these methods is superior (or, is there another method I should consider beyond these two)?
 
Bartender
Posts: 4179
22
IntelliJ IDE Python Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I like bits and pieces of both methods. The benefit of the anonymous inner class approach is that it lets you implement multiple listeners in one class, allows you to keep the methods which perform the work under your control (make them private if you need to), and helps hide some implementation details (that you use actionListener instead of javax.swing.Action, for example). The downside is that you get large super classes and hard to read code.

The second approach makes the class a bit more modular, but exposes a lot of detail, including a means to access the work outside of the manner you intend (someone could call actionPerformed themselves).

A better approach, in my opinion is a bit of a combination:


The Eavesdropper then is not tied to using an actionListener, no one can trigger its doEavesdropping method without going through whatever method you define (Action, or Button). And it can contain several (hopefully related) listeners as well. Note you would pass in both the button and the target in this case.
 
Rancher
Posts: 2759
32
Eclipse IDE Spring Tomcat Server
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The first way is Java way of doing closures. It's equivalent of passing the method to the button.

The second way is useful if there s going to be some reuse across classes, which usually doesn't happen. Most of the time the first way works
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Steve Luke wrote:A better approach, in my opinion is a bit of a combination:


The Eavesdropper then is not tied to using an actionListener, no one can trigger its doEavesdropping method without going through whatever method you define (Action, or Button). And it can contain several (hopefully related) listeners as well. Note you would pass in both the button and the target in this case.


That's pretty clever! Nice way of using the anonymous inner class to hide the listener, while still breaking it out of the class that generates the event. Could you do the same thing with named, private inner classes?
 
Marshal
Posts: 80775
489
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
If the serach thing is working, you find there are several ways to write Listeners. There are classes which extend AbstractAction, too, which is nice. If our search facility is working, you will find this sort of post, this, and this. You may get the impression I think addActionListener(this) is bad programming. If you get that impression, it is well‑founded
If you follow links in those posts, you will find other ways to write Listeners. But they are all on the GUI forum, so I shall send this discussion to GUIs to join them.
 
Ranch Hand
Posts: 47
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote: The anonymous inner class has the advantage of direct access to all of the enclosing object's fields and methods. But it requires a method-within-a-method approach (with the actual event handler being called from inside the anonymous inner class's actionPerformed method), and (I am noticing) tends (in my own case, anyway) to lead to large class files with lots and lots of event handlers (one for every menu item, one for every button, and one for everything any other Swing control might generate as an event I need to handle).



A couple things to note - an inner class (named, not anonymous) also has direct access to all of the enclosing object's fields and methods, regardless of access modifier. You could use a private inner class that implements the listener as another possibility.

Also the anonymous class way of implementing the listener does not necessarily require a method-within-method approach, the method could be defined entirely within the anonymous class if you were hand coding the GUI - I have never used NetBeans though.

I think both of these approaches are considered superior to the way Oracle does it in many of their tutorials (ie- public class Demo extends JPanel implements ActionListener).
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Steve Myers wrote:Also the anonymous class way of implementing the listener does not necessarily require a method-within-method approach, the method could be defined entirely within the anonymous class if you were hand coding the GUI - I have never used NetBeans though.


Yes, that's a good point too. No problems if each anonymous inner class has its own actionPerformed method.

There are always a lot of programmers disdainful of IDEs, and I suppose this is one of the reasons for that: they tend to impose conventions that aren't always what you want. The method-within-method convention is something I doubt I'd have thought of myself. I only know about it because it's what NetBeans does.

I think both of these approaches are considered superior to the way Oracle does it in many of their tutorials (ie- public class Demo extends JPanel implements ActionListener).


Please help me be sure I understand what you're saying. Are you saying it is considered superior to the addActionListener(this) approach, or to the entire idea of of using an external object as your listener (as opposed to an inner class object)?
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:If the serach thing is working, you find there are several ways to write Listeners. There are classes which extend AbstractAction, too, which is nice. If our search facility is working, you will find this sort of post, this, and this.



Sorry I didn't find those. I was having an atypically difficult time locating help by searching on this issue. Couldn't seem to come up with a good query. Thanks for the links.

You may get the impression I think addActionListener(this) is bad programming. If you get that impression, it is well‑founded


Yeah, I think I get you on that one. Those big switch statements (or cascades of if/else if statements) remind me of early Windows code. Ick.

If you follow links in those posts, you will find other ways to write Listeners. But they are all on the GUI forum, so I shall send this discussion to GUIs to join them.


Thanks for doing that. Makes sense.

In one of your posts on the topic, you said, "For a Listener you are using once and once only, use an anonymous inner class. No two ways about it." I can see why you could not us an anonymous class for a listener you want to use more than once (since you wouldn't have the reference to it that you'd need to pass to the second call to addActionListener), but what's the advantage of anonymous inner classes over external classes? In my application, I have a lot of event handlers, some of which are more than just a few lines. Collectively, all those anonymous inner classes are accumulating a lot of lines of code in one class file. I'm always reading this or that "rule of thumb" that says my class files ought not to be longer than n * 100 (where "n" is 1, 2, 10, or whatever number the person offering the rule probably finds their own class files tend to be) lines long. The class file that has me looking at this issue had grown to about 4,000 lines because of this. What I have done is move all those listeners into classes of their own, each extending AbstractAction. The original class file is now much smaller, and each listener class is a tidy little file of its own, but... I'm not at all sure I've really gained anything, other than the emotional satisfaction that comes from meeting my objective of having small class files.

I suppose one advantage of named external classes is that, if you later do want a listener to be able to handle events from more than one source, you already have that listener defined as a class. Is that insufficient reason to put all of my listeners into class files?

Thanks for the help, btw.
 
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

I suppose one advantage of named external classes is that, if you later do want a listener to be able to handle events from more than one source, you already have that listener defined as a class. Is that insufficient reason to put all of my listeners into class files?


You have to be careful you aren't trading a very long class file which is difficult maintain for a package with so many classes in it it's difficult to maintain.

I think if you asked a half a dozen different experienced Java coders about writing listeners you'd get half a dozen different answers. The one common theme being don't use addActionListener(this) except in very rare circumstances.
Personally I tend to work to the following guidelines:

1. If the listener is unique to the control and only requires a few lines of code use an anonymous inner class.
2. If the listener is unique to the control and requires access to variables not visible in the anonymous inner class, use an inner class and pass a reference to the local variable(s) in the inner classes constructor.
2. If the listener is unique to the control and but requires a lot of complex code use an inner class.
3. If the listener may be used by more than one control in this class use an inner class.
4. If the listener may be used in more than just this class use an external class.
 
Steve Myers
Ranch Hand
Posts: 47
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:

Steve Myers wrote:

I think both of these approaches are considered superior to the way Oracle does it in many of their tutorials (ie- public class Demo extends JPanel implements ActionListener).


Please help me be sure I understand what you're saying. Are you saying it is considered superior to the addActionListener(this) approach, or to the entire idea of of using an external object as your listener (as opposed to an inner class object)?



The one form (used in some Oracle tutorials) you want to avoid is something like



This exposes the method to the outside world
The others are ok in the right circumstances as Tony D describes
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Steve Myers wrote:The one form (used in some Oracle tutorials) you want to avoid is something like


Gotcha. Color me convinced.
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
How does this approach look?

My main class is a JFrame with a "File" menu and two menu items, "Open" and "Close." It creates two listener objects and assigns them to the menu items, passing itself as an argument to the listener constructor:

Note that the listener objects are both inner classes of a single parent class called "FileListener." That parent class is defined thus:
I've tried to retain some of Steve Luke's approach (which prevents external access to the actionPerformed methods) by making the parent class's access level package-private. I hope this means only classes within the package can create instances of the inner classes that provide the ActionListener objects.

This splits out the listener code from the object that has the sources of the events, while not creating as many class files as there are events to listen for. It seems like a natural structure, at least for grouped objects that are event sources, like menu items. It still produces an abundance of class definitions, with one for every listener (as well as one for every group of listeners), but at least I don't have a zillion (the usual number, for me) class files to deal with.

Would this be a workable structure, or is it needless complexity in the service of another false god?
 
Steve Luke
Bartender
Posts: 4179
22
IntelliJ IDE Python Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Stevens Miller wrote:

Steve Myers wrote:Also the anonymous class way of implementing the listener does not necessarily require a method-within-method approach, the method could be defined entirely within the anonymous class if you were hand coding the GUI - I have never used NetBeans though.


Yes, that's a good point too. No problems if each anonymous inner class has its own actionPerformed method.


I personally try to avoid putting the code directly in the anonymous inner class (in more than demo purposes). The reason is you hide the action you want to perform inside your GUI code. It makes it hard to find, and hard to modify later on. The method call from the anonymous inner class is more manageable, in my opinion.

Stevens Miller wrote:



One of the reasons to use the external class is to separate the GUI code (your JFrame) from your business logic and control (the work that needs to be done, and marshaling commands to the place where the work should be done). The doFileOpen() and doFileClose() methods should probably be moved out of your GUI class unless the methods are trivial (and don't get used to putting trivial methods into the GUI code either - because they soon become non-trivial). And, if you do put the methods in the GUI code then having the FileListener as a separate class is an extra layer of indirection that doesn't really add anything (because the working code is still in the GUI class). It is an increase in complication for no benefit, so I would suggest axing it.
 
Stevens Miller
Bartender
Posts: 1464
32
Netbeans IDE C++ Java Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Steve Luke wrote:One of the reasons to use the external class is to separate the GUI code (your JFrame) from your business logic and control (the work that needs to be done, and marshaling commands to the place where the work should be done). The doFileOpen() and doFileClose() methods should probably be moved out of your GUI class unless the methods are trivial (and don't get used to putting trivial methods into the GUI code either - because they soon become non-trivial). And, if you do put the methods in the GUI code then having the FileListener as a separate class is an extra layer of indirection that doesn't really add anything (because the working code is still in the GUI class). It is an increase in complication for no benefit, so I would suggest axing it.


Yeah, that makes sense. My example is kind of misleading in that regard, as the external classes are all trivial and do just call back into the JFrame object, giving the impression that, just as you say, I'm not gaining anything. But, in actual practice, I would do as you advise, putting my business logic in the external class methods, only calling back into the JFrame when I needed to store a final result (or, maybe, obtain some state from the JFrame object that I need to proceed with that same external business logic).

Assuming I do things that way, how do you feel about using parent classes to group inner classes into sets of functionally related listeners? All else being equal, would you see that as an improvement over having one external class for each listener?
 
reply
    Bookmark Topic Watch Topic
  • New Topic