JavaData & JavaUnderstanding Java Observable and JavaFX Observable

Understanding Java Observable and JavaFX Observable

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

The Observable and Observer objects play an important role in implementing Model-View-Controller architecture in Java. They are typically used in a system where one object needs to notify another about the occurrences of some important changes. Observable is a class and Observer is an interface. They are found in the java.util package as a part of the Java Core Utility Framework. However, the Observable entity found in the JavaFX is an interface and is a part of the javafx.beans package. The basic idea is the same, yet the implementation varies. This article shall try to explain the concepts behind the paradigm and how they are used in Java code with simple examples.

Java Observable and Observer

Any Java class interested in being observable extends the Observable class. The extended class then can be observed by other interested classes in other parts of the program. The registered classes are notified as soon as any change is triggered in the observable class to which the observer class is registered to. This is the relation between the Observable and Observer classes. Observer classes are nothing but the implementation of the Observer interface. The Observer interface provides an update() method that is called when an observer is notified of a change in the observed object.

A class that wants to be observed or the class that extends Observable must call the setChanged() method if it has changed and must trigger notification by calling the notifyObservers() method. This in turn calls the update() method of the observer class. If, however, an object calls the notifyObserver() method without calling the setChanged() method, no action takes place. Therefore, an observable object must call the setChange() method prior to calling the notifyObservers() method on the event of any change. This order would subsequently call the update() method of the observer classes seamlessly.

A Quick Example

The example here gives an idea about how the observer and observable is implemented. The PeopleObserver class implements the Observer interface, and the PeopleObservable class extends the Observable class. PeopleObservable objects are monitored by a list of observable objects defined by the PeopleObserver class. Once a change occurs in the observable class, it notifies the observers as observer objects are added to the list by the addObserver() method. Every call to notifyObserver() triggers a call to the update() method of the observer subsequently.

package org.mano.example;

import java.util.Observable;
import java.util.Observer;

public class PeopleObserver implements Observer {

   private String name;

   public PeopleObserver() {
      super();
   }

   public PeopleObserver(String name) {
      super();
      this.name = name;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   @Override
      public void update(Observable o, Object arg) {
         System.out.println(name + " got " +
            ((Integer) arg).intValue() + " winks");
      }

}


package org.mano.example;

import java.util.Observable;

public class PeopleObservable extends Observable {

   private String name;

   public PeopleObservable() {
      super();
   }

   public PeopleObservable(String name) {
      this.name = name;
   }

   public String getName() {
      return name;
   }

   public void setName(String name) {
      this.name = name;
   }

   void wink(int count) {
      setChanged();
      notifyObservers(count);
   }
}



package org.mano.example;

import java.util.Random;

public class Main {

   public static void main(String[] args) {

      PeopleObservable damsel = new PeopleObservable("Minnie");

      PeopleObserver mickey = new PeopleObserver("Mickey");

      PeopleObserver donald = new PeopleObserver("Donald");

      PeopleObserver guffy = new PeopleObserver("Goofy");

      damsel.addObserver(mickey);
      damsel.addObserver(donald);
      damsel.addObserver(goofy);

      System.out.println("There are " + damsel.countObservers()
                                      + " observers of "
                                      + damsel.getName());

      Random r = new Random();
      damsel.wink(r.nextInt(10));
   }
}

JavaFX Observable

The JavaFX Observable interface is the base interface for many container classes and interfaces in the JavaFX Collection Framework. For example, ObservableList<E> and ObservableArray<T> are two common implementations of the base Observable interface. The Java core Collection API already contains many useful container classes to represent the generic data structure of lists, set, and maps. For example, java.util.ArrayList<E> is a re-sizable array implementation of the java.util.List<E> interface to contain a list of objects. However, they are incapable of working seamlessly when synchronous functionality is required between the list model and the view component in a GUI scenario.

Before JavaFX, Swing developers relied on ArrayList to contain a list of objects and subsequently display them in a list-like UI control, such as JList. But, ArrayList is too generic and was not built keeping in mind the requirement of synchronization when associated with a view component. As a result, it is quite difficult to update, add, or remove objects from the model list and at the same time reflect changes in the view component. To overcome this problem, JavaFX uses observable interfaces and their implementation, such as ObserverList<E>.

Because ObservableList<E> adheres to the rules of the observable and observer paradigm of MVC, such as providing notification to its interested observer regarding any updation, addition or removal of objects from the model list, it became a de-facto container for using any lists in the JavaFX arena. Here, data representation in the model and view are synchronized seamlessly. JavaFX ObserverList<E> is typically used in UI controls such as ListView and TableView. Let’s go through a quick example to see how ObservableList<E> is actually used.

A Quick Example

This is a simple example where a list of data is displayed in a JavaFX UI control called ListView. The underlying data model used is ObservableList. There are two ListView controls used to move data from one control to another, and two ObservableLists represent the data of the underlying model. The data not only moves from one control to another visually, but it also moves from one model to another. This makes data transition between the view and the model layers synchronous.

package org.mano.example;

import javafx.application.Application;
import javafx.collections.*;
import javafx.event.*;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;

public class ObservableListdemo extends Application {

   private final ObservableList<String> countries;
   private final ObservableList<String> capitals;

   private final ListView<String> countriesListView;
   private final ListView<String> capitalsListView;

   private final Button leftButton;
   private final Button rightButton;

   public ObservableListdemo() {
      countries = FXCollections.observableArrayList("Australia",
         "Vienna", "Canberra", "Austria", "Belgium","Santiago",
         "Chile", "Brussels", "San Jose", "Finland", "India");
      countriesListView = new ListView<>(countries);
      capitals = FXCollections.observableArrayList("Costa Rica",
         "New Delhi", "Washington DC", "USA", "UK", "London",
         "Helsinki", "Taiwan", "Taipei", "Sweden", "Stockholm");
      capitalsListView = new ListView<>(capitals);

      leftButton = new Button(" < ");
      leftButton.setOnAction(new ButtonHandler());

      rightButton = new Button(" > ");
      rightButton.setOnAction(new ButtonHandler());

   }

   public static void main(String[] args) {
      launch(args);
   }

   @Override
   public void start(Stage primaryStage) {
      primaryStage.setTitle("Arrange Countries and Capitals");
      BorderPane root = new BorderPane();
      Scene scene = new Scene(root, 500, 450);

      GridPane gridPane = new GridPane();
      gridPane.setPadding(new Insets(5));
      gridPane.setHgap(10);
      gridPane.setVgap(10);
      ColumnConstraints column1 = new ColumnConstraints(150, 150,
         Double.MAX_VALUE);
      ColumnConstraints column2 = new ColumnConstraints(50);
      ColumnConstraints column3 = new ColumnConstraints(150, 150,
         Double.MAX_VALUE);
      column1.setHgrow(Priority.ALWAYS);
      column3.setHgrow(Priority.ALWAYS);
      gridPane.getColumnConstraints().addAll(column1, column2, column3);

      Label countriesLabel = new Label("Countries");
      GridPane.setHalignment(countriesLabel, HPos.CENTER);
      gridPane.add(countriesLabel, 0, 0);

      Label capitalsLabel = new Label("Capitals");
      GridPane.setHalignment(capitalsLabel, HPos.CENTER);
      gridPane.add(capitalsLabel, 2, 0);

      gridPane.add(countriesListView, 0, 1);
      gridPane.add(capitalsListView, 2, 1);

      VBox vbox = new VBox();
      vbox.getChildren().addAll(rightButton, leftButton);

      gridPane.add(vbox, 1, 1);

      root.setCenter(gridPane);
      GridPane.setVgrow(root, Priority.ALWAYS);
      primaryStage.setScene(scene);
      primaryStage.show();
   }

   private class ButtonHandler implements EventHandler<ActionEvent> {

   @Override
   public void handle(ActionEvent event) {

      if (event.getSource().equals(leftButton)) {
         String str = capitalsListView.getSelectionModel()
            .getSelectedItem();
         if (str != null) {
            capitals.remove(str);
            countries.add(str);
         }
      } else if (event.getSource().equals(rightButton)) {
         String str = countriesListView.getSelectionModel()
            .getSelectedItem();
         if (str != null) {
            countriesListView.getSelectionModel().clearSelection();
            countries.remove(str);
            capitals.add(str);
         }
      }
   }
}

Output

Observe
Figure 1: Output of the preceding code

Conclusion

The Observable class and the Observer interface provide a invaluable impetus to implement sophisticated program architectures based on the Model-View-Controller paradigm. In fact, the observable and observer concept is more than just MVC. A good understanding of the underlying concept can help create more sophisticated code in Java. If observer and the observable portions of the Java core utility framework show the principle, the JavaFX observable classes and interfaces exactly show how those principles can be put to work.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories