views:

272

answers:

7

Say you have the following java bean:

public class MyBean
{
    private List<String> names = new ArrayList<String>();

    public void addName(String name)
    {
        names.add(name);
        fireNamesPropertyChange(name);
    }
}

How would you normally implement a property change event for a collection? Do you try and use the index property which seems to be more for arrays than collections?

A: 

For a swing GUI event I'd normally just use an EventListenerList to do the work for me.

EDIT: on the rephrase of the questions: how do you treat collections, I'd usually use an event similar to the collections type, so for example a TreeModel event usually takes a TreePath argument, or for something in a map I'd indicate the key.

However for simple JavaBeans the most common is assume a list/array and just use the index.

Nick Fortescue
I am not interested in how the listeners are stored. What I am looking for is an alternative to PropertyChange that can handle Collections.
willcodejavaforfood
and note that this is not Swing specific
willcodejavaforfood
A: 

Methinks you will need fireNamesPropertyAdd, fireNamesProperyDelete. A list level notification will IMHO not work, even if it was an array and an index was added as it can't handle deletes. If the element at some index can be changed, you will also need fireNamesProperyChange. It might be useful to have index as parameter in addition to the string value.

Hemal Pandya
+1  A: 

(NOTE: I updated this post after realizing a few mistakes of my own so this isn't the original but a more refined one instead)

For this purpose I'd do two new interfaces, ListListener and Listenable and then I would create a new class like ListenableArrayList which would wrap every List method with a call to one (or more) relevant methods defined in ListListener. In code it'd be something like this:

public class ListenableArrayList<T> extends ArrayList<T>
                                    implements Listenable<T> {

    private ArrayList<T> internalList;
    private ListListener<T> listener;

    /* .. */

    public void add(T item) {
     listener.beforeAdd(T item);
     internalList.add(item);
     listener.afterAdd(T item);
    }

    /* .. */

    public void setListener(ListListener<T> listener) {
     this.listener = listener;
    }

}

public interface ListListener<T> {
    /* .. */
    void beforeAdd(T item);
    void afterAdd(T item);
    /* .. */
}

public interface Listenable<T> {
    /* .. */
    void setListener(ListListener<T> listener);
    /* .. */
}

The reason I'd do it this way would be to allow for creating truly ad-hoc listeners on the fly instead of tying the ListenableArrayList to some specific implementation. For example with this the following would be possible:

Listenable<String> list = new ListenableArrayList<String>();

list.setListener(new ListListener<String>() {
    @Override
    public void beforeAdd(String item) {
     System.out.println("About to add element "+item+"...");
    }
    @Override
    public void afterAdd(String item) {
     System.out.println("...element "+item+" has been added.");
    }
});

A bit cluttered, maybe but on the other hand this would allow for easy extension to Collections, Sets and whatnot rather easily.

Esko
...you could of course just call the listener methods directly too, I'm just trying to be as clear as possible with the example.
Esko
List<String> list = new ListenableArrayList<String>();((Listenable)list).setListener(new ListListener() {? Icky!
Tom Hawtin - tackline
Scary that's the only thing you're complaining about since there's another, more important issue I just noticed. I'll update the post in a minute.
Esko
OK, updated the post. No more casting! :P
Esko
Oh and as for anonymous inner classes, it's a great way to prototype things. Eventually I'd refactor it away from there but it's not my priority to make beautiful code at the first time, I want correctly behaving code first.
Esko
+3  A: 

Take a look at Glazed Lists library, which has support for observable collections.

If I were to do it myself, I would likely create custom Listener interface with elementsAdded, elementsRemoved methods, or similar :-) (also depending on my needs)

Peter Štibraný
That's what I normally do as well. Just curious what everyone else were doing. At least I got some nice link to Collection that are actually oberserable. :)
willcodejavaforfood
A: 

Are you perhaps looking for java.beans.PropertyChangeSupport?

In my opinion, you should avoid PropertyChangeEvent. IndexedPropertyChangeEvent is worse, and very infrequently used by Swing anyway. It's better to narrow the focus of your types, and fire a javax.swing.event.ChangeEvent or similar (even just call a Runnable).

For certain types (like lists!), Glazed Lists (or equivalent) mentioned in another post by Peter Štibraný seem like a good way to go.

Tom Hawtin - tackline
I am looking for an alternative to it :)
willcodejavaforfood
+1  A: 

Normally I'd do the following:

public class MyBean {
    private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private List<String> names = new ArrayList<String>();
    public void addName(String name) {
        names.add(name);
        pcs.firePropertyChange("names", null, Collections.unmodifiableList(names));
    }
    public void addPropertyChangeListener(PropertyChangeListener l) {
        pcs.addPropertyChangeListener(l);
    }
    public void removePropertyChangeListener(PropertyChangeListener l) {
        pcs.removePropertyChangeListener(l);
    }
}

PropertyChangeSupport manages the listeners and fires the events on your behalf.

By passing null as the "old value" it forces the event to be fired. (It's likely that listeners won't really care about the old value anyway)

Scott Stanchfield
I got so obsessed with indexed properties that I didnt even think of that. That works fine when you dont really care about wheather an object was added/removed just that somethign changed.
willcodejavaforfood