views:

737

answers:

4

OK, so if I add an ActionListener to a GUI element, and it's the only element I use that ActionListener with, does it matter which of the following lines (a,b) I use to get the checkbox selected state?

final JCheckBox checkbox = (JCheckBox)this.buildResult.get("cbDebugTick");
checkbox.addActionListener(new ActionListener() {
 @Override public void actionPerformed(ActionEvent event){    
   boolean bChecked =
   // (a) checkbox.isSelected();
   // (b) ((JCheckBox)event.getSource()).isSelected();
   model.setPrintDebugOn(bChecked);
  }
});

It makes sense to me that if I add the ActionListener object to multiple GUI elements, then I should use (b).

And in (b), is it OK to blindly cast event.getSource() to JCheckBox, since I'm the one who added the action listener, or should I program defensively and do an instanceof check?

note: this question is in the context of event listeners in general; kdgregory has some good points below specifically re: checkboxes which I had neglected to consider.

A: 

I'd program with b defensively as it's the best-practice option. But if only you are ever going to use the code then there is no reason why you can't do a. However, imagine how happy you will be with yourself if you come back to it at some future point, change something and find you wrote good code which you can directly reuse...

Smalltown2000
A: 

in (b) to be rigourous, you should indeed do a instanceof check, but it's not that important. I would think both these lines are fine and acceptable, though (b) would be "better code"

Although, what is usually done in an action listener is simply call another method customized to your checkbox. So it would look like something like this:

 @Override public void actionPerformed(ActionEvent event) {                                  
    //your treatment would be in this method, where it would be acceptable to use (a)                  
    onCheckBoxActionPerformed(event)
}
David Menard
could you elaborate? why the forwarding to the outer class if you can have access to the same methods/fields in an anonymous inner class?
Jason S
this way you can easily add different components to the same action listener and treat them differently and/or polymorphicaly, making the code "better"
David Menard
I disagree with david's assertion. Forwarding a call like this goes against the grain of de-coupling (which is why a listener approach was adopted for event notification in Swing in the first place). The ideal case is where you can factor out the action behavior entirely, and use the action listener external to the view that contains the control - in this case, option (b) is the preferred technique. That said, what you *really* should be doing is creating an Action and attaching the control to it.
Kevin Day
FYI, this is the way most IDEs generate the automated code when using a grpahical GUI creator, ex.: NetBeans
David Menard
FWIW, most GUI code generated using a graphical tool sucks.
bcat
+2  A: 

I'd do neither.

If clicking the checkbox is going to start some action, I'd attach an ItemListener, then just look at the selection state in the ItemEvent.

However, checkboxes don't normally invoke actions, they manage state. So a better approach is to examine all of your checkboxes in response to whatever does kick off the action.


Edit: some commentary about the larger issues that the OP raised.

First, it's important to realize that large parts of Swing represent implementation convenience rather than a coherent behavior model. JCheckBox and JButton have nothing in common other than the fact that clicking within their space is meaningful. However, they both inherit from AbstractButton, which provides implementation details such as the button's label. It also assumes that buttons are "pressed", and that pressing a button will initiate some meaningful behavior (the action). In the case of JCheckbox, however, the button press is not important, the change in state is. That state change is signaled to the ItemListener -- which is also defined on AbstractButton even though state changes are meaningless to other button types (the JavaDoc even says "checkbox").

One of the things that Swing did get right -- if hard to use -- is the idea of that an Action is separate from the control initiating that action. An Action object can be invoked from multiple controls: a menu item, a pushbutton on a dialog, a keystroke, whatever. More important from a design perspective is that it takes you away from the idea of a generic "listener" that tries to figure out what needs to happen. I've seen apps where a single listener receives input from the entire menu system, for example, and then runs through a big if/else chain to figure out which menu item was pressed. Using Actions means you have more classes, but in the long run gives you a more maintainable app.

Finally, from a usability perspective, there's a difference between controls that maintain state, such as JCheckbox and JTextArea, and those that initiate actions, such as JButton and JMenuItem. I have seen a (web) app where clicking on a radio button takes you to a different page. That's bad. Even if you're planning to use listeners internally, to update the state of some model, you should ask yourself why the collection of GUI elements do not in themselves provide you with a model.

kdgregory
a valid point... but that wasn't my question
Jason S
True, but possibly there will be another person who finds this question via Google, and would prefer to use the API appropriately, rather than worry about which of two hacks is "better."
kdgregory
that's fair, I'll note that in the question. +1 for the other comment at the end: in this case I was using the listener to propagate checkbox state to a model object that didn't have knowledge of the checkbox, I guess I should use javax.swing.JToggleButton.ToggleButtonModel instead of an event listener.
Jason S
I also should have asked my question in terms of a JButton or other JComponent; this issue has come up before for me with event listeners and I want to know the appropriate good practice.
Jason S
+1  A: 

For the case where the listener is exclusive (such as an anon listener), I use (a).

If the listener will be reused (eg, this is an instance of ActionListener) I'll write it as:

@Override
public void actionPerformed(ActionEvent event) {
    Object src = event.getSource();
    if (src == checkbox) {
        boolean bChecked = checkbox.isSelected();
        // ...
    }
}

If you have several checkboxes and they are processed the same way, then instanceof makes sense.

Devon_C_Miller