Question

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?

Was it helpful?

Solution

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)

OTHER TIPS

(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.

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)

JDK 7+ solution:

import javafx.collections.*;
import java.util.*;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("s1");
        list.add("s2");

        ObservableList<String> observableList = FXCollections.observableList(list);
        observableList.addListener(new ListChangeListener<String>() {
            @Override
            public void onChanged(Change<? extends String> change) {
                while(change.next()){
                    System.out.println("added: " + change.getAddedSubList());
                }
            }
        });

        observableList.add("s3");
    }
}

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.

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.

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.

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