Why doesn't ChangeListener's getSource() return a pre-cast object of the type used to generate an event?

StackOverflow https://stackoverflow.com/questions/17846838

  •  04-06-2022
  •  | 
  •  

Question

Instead of having to cast the ChangeEvent Object back into a JSpinner Object so that I can access the getValue() method, why doesn't e.getSource() simply return a pre-cast object of the type used to generate the event? The first usage of e.getSource() makes it clear that it KNOWS a JSpinner is being used, so why not just cast it back? It seems like it'd be simpler. I'm assuming there are safety reasons not to do this, but I'd like to see or hear of a concrete example why that would be a bad thing to have as the default usage.

Is it unsafe? Is it just bad architecture?

public class SpinnerPanel extends JPanel implements ChangeListener {

...

SpinnerPanel()
{
    birthdaySpinner.addChangeListener(this);
    add(birthdaySpinner);
}

@Override
public void stateChanged(ChangeEvent e) 
{
    System.out.println(e.getSource());

    JSpinner spinner = (JSpinner)e.getSource();
    System.out.println(spinner.getValue());
}
}
Was it helpful?

Solution

The fact that the source will be a JSpinner is known at runtime. The kind of “automatic type casts” you describe are possible with generics at compile time, but at that time there is no telling where the listener might end up. For example, you could take your spinner, obtain a list of all listeners, and then add these listeners to another widget like a check box. There is nothing in your code to tell the compiler at compile time that this won't happen, so there is nothing to tell the compiler that the source will always be a spinner.

It would theoretically be possible to use generics in some way. You could have a ChangeEvent<T> for changes originating from a component of class T. You could have a ChangeListener<T> with a method stateChanged(ChangeEvent<? extends T>). But then what would be the type for all listeners of a given component of class T? If it were List<ChangeEvent<T>> then you wouldn't be allowed to add a generic ChangeListener<Object> to that list. So it would have to be List<ChangeEvent<? super T>>. But if you had such a list in any component, then it would be the same type for all derived components, instead of a more specific type like the generics approach would suggest.

So on the whole I think this generics approach would make the code a lot harder to write and maintain, would likely leave loopholes in some places, would probably be horrible in terms of backwards compatibility, and on the whole would not be worth the effort.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top