7

Initial Starting Point

I have an existing List of 1000 Person objects which I would like to insert an Extractor to listen for property changes within any Person objects (this ObservableList will be later attached to a TableView).

So my code would be like:

ObservableList<Person> observablePersons = FXCollections.observableList(personlist,
    personextractor);

Error Message

But when I try to add a new person objects to this ObservableList observablePersons, I run into this error:

run:
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:148)
    at com.sun.javafx.collections.ObservableListWrapper.doAdd(ObservableListWrapper.java:101)
    at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:151)
    at java.util.AbstractList.add(AbstractList.java:108)
    at test.listchangelistener.listChangeDemo.main(listChangeDemo.java:72)
Java Result: 1

Could you please tell me why would I come across this error message? My java version is jdk1.8.0_91 (32-Bit)

Person Class

package test.listchangelistener;

import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

class Person {

    private final IntegerProperty age = new SimpleIntegerProperty();
    private final StringProperty name = new SimpleStringProperty();

    public Person(String name, Integer age) {
        setName(name);
        setAge(age);
    }

    public int getAge() {
        return age.get();
    }

    public final void setAge(int value) {
        age.set(value);
    }

    public IntegerProperty ageProperty() {
        return age;
    }

    public String getName() {
        return name.get();
    }

    public final void setName(String value) {
        name.set(value);
    }

    public StringProperty nameProperty() {
        return name;
    }

    @Override
    public String toString() {
        return "Person{" + "age=" + age.get() + ", name=" + name.get() + '}';
    }

}

Test Code

package test.listchangelistener;

import java.util.Arrays;
import java.util.List;
import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.util.Callback;


public class listChangeDemo {
    public static void main(String[] args) {

        Person p1 = new Person("Ted", 26);
        Person p2 = new Person("Anne", 19);

        // just a simple list
        List<Person> persons = Arrays.asList(p1, p2);

        // extractor to observe change of person properties
        Callback<Person, Observable[]> extractor = (Person p) -> {
            return new Observable[]{
                p.ageProperty(),
                p.nameProperty()
            };
        };

        // make list observable and attach extractor
        ObservableList<Person> observablePersons = FXCollections.observableList(persons, extractor);

        // create listchangeListener for observableList
        ListChangeListener listener = (ListChangeListener) (ListChangeListener.Change c) -> {
            while (c.next()) {
                if (c.wasAdded()) {
                    System.out.println("these were added: ");

                    List addedSubList = c.getAddedSubList();
                    addedSubList.forEach((Object t) -> {
                        System.out.println("added Person: " + t);
                    });
                } else if (c.wasRemoved()) {
                    System.out.println("these were removed");

                    List removedSubList = c.getRemoved();
                    removedSubList.forEach((Object t) -> {
                        System.out.println("removed Person: " + t);
                    });

                } else if (c.wasUpdated()) {
                    System.out.println("these were updated");

                    System.out.println("Updated elements are: "
                            + c.getList().subList(c.getFrom(), c.getTo()));
                }
            }
        };

        // attach listchangeListener to observableList
        observablePersons.addListener(listener);

        // testing changes
        observablePersons.add(new Person("Siegfried", 10));

    }
}
Draken
  • 3,134
  • 13
  • 34
  • 54
Chiggiddi
  • 542
  • 1
  • 8
  • 26
  • 1
    I don't think this question should be marked as a duplicate. While the underlying problem ends up being the same, the situations are different. In my opinion, the addition of observable list in this case makes it a unique question – Danny Harding Dec 19 '17 at 16:46

1 Answers1

13

The problem is coming from the creation of your backing List:

List<Person> persons = Arrays.asList(p1, p2);

If you take a look on the javadoc of Arrays.asList:

Returns a fixed-size list backed by the specified array.

On a closer look you got an UnsupportedOperationException because List.add() is an optional operation:

Throws: UnsupportedOperationException - if the add operation is not supported by this list.

You can update the creation of the backing list as:

List<Person> persons = new ArrayList<Person>(Arrays.asList(p1, p2));

The difference is that the returned ArrayList is an exact, independent copy of the passed one, therefore the restriction described above is not valid anymore in this case.

You can also take a look on this question which explaines this topic in depths:

Difference between Arrays.asList(array) vs new ArrayList<Integer>(Arrays.asList(ia)) in java

Graham
  • 7,431
  • 18
  • 59
  • 84
DVarga
  • 21,311
  • 6
  • 55
  • 60
  • Hi DVarga! Thanks so much for your fast reply! Yes, I totally overlooked the restriction of the Arrays.asList(). That was so stupid of me. Anyways thanks a ton! – Chiggiddi Jul 04 '16 at 11:55