In the spirit of Neil and Josh/Will’s Java Puzzlers, here is one I just recently ran into:
Given the following block of code:
PropertyChangeSupport changes = new PropertyChangeSupport(this);
PropertyChangeListener l = new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent evt) {
throw new UnsupportedOperationException("Not supported yet.");
}
};
changes.addPropertyChangeListener("foo", l);
for( PropertyChangeListener remove : changes.getPropertyChangeListeners() ){
changes.removePropertyChangeListener(remove);
}
System.out.println(changes.getPropertyChangeListeners().length);
What will this snippet print:
- 0
- 1
- Throws an Exception
- Other
Answer after the jump.
The answer is, it prints “1″.
When you add a PropertyChangeListener to the PCS class using a property name, the listener is returned in the .getPropertyChangeListeners() array. However, you can only remove a listener added in this way using the .removePropertyChangeListener( “propertyName”, listener) method.
The PropertyChangeListenerProxy class provides a self-filtering on events based on property name which you can subclass and add it using the unindexed .addPropertyChangeListener(listener) method. Without looking at the internals of PropertyChangeSupport, it seems to me this would get noisy if you had a lot of listeners, though. Since all events are going to all of the proxies then they are filtering before they call the .propertyChange(PropertyChangeEvent evt) method, rather than only firing events to the listeners that care about them.
update: There are actually some stranger thing about this issue. Scan the comments for more information!



Hi, I try it at JAVA6, the answer is 0.
gives me 0 with 1.5.0_09 too
Yup. I guessed 0, ran it, and got 0...
Interesting. Yeah. I get differing results between JVMs!
Wait. This gets weirder.
Something changes when you call the getPropertyChangeListeners(). Running 1.5 on my Mac, the above code returns 0. This code, however, returns 1.
Main(){
PropertyChangeSupport changes = new PropertyChangeSupport(this);
PropertyChangeListener l = new PropertyChangeListener(){
public void propertyChange(PropertyChangeEvent evt) {
throw new UnsupportedOperationException("Not supported yet.");
}
};
changes.addPropertyChangeListener("foo", l);
changes.removePropertyChangeListener(l);
System.out.println(changes.getPropertyChangeListeners().length);
}
I suspect, as you alluded to in the original post, it has something to do with the fact that in your second example, the "listener" that gets stored in the PropertyChangeListeners list is an instance of java.beans.PropertyChangeListenerProxy (verified by printing out getClass().getCanonicalName() for the entries in the getPropertyChangeListeners() list). ie. it's a different object to the one that was inserted into the array, so attempting to remove your original object will fail.
On the face of it, it does appear a bit counter-intuitive, and the API is not particularly well specified (see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4937059 for some other examples of problems with the PropertyChangeSupport documentation).