Java Quiz Player

Java PropertyChangeListener as an Observer

Apr 3, 2019

1. Overview

This blog's article dated Jul 23, 2013, Java Swing Example using Observer and Observable discussed about using java.util.Observer and java.util.Observable classes and the Observer and Model View Controller design principles.

As of Java 9 both these classes are deprecated. The PropertyChangeListener interface can be used as an observer in a similar manner.

2. Description

2.1. PropertyChangeListener

java.beans.PropertyChangeListener is a functional interface (it has one abstract method, the functional method, and further this can be a used as a lambda expression).

This is a listener interface for receiving property change events, i.e., whenever a bean (as in Java Bean) changes a bound property. This interface method propertyChange is called when the bound property is changed. The method takes a PropertyChangeEvent parameter which has details about the event source and the property that has changed (property name, previous value and the changed value).

2.2. PropertyChangeSupport

java.beans.PropertyChangeSupport, a utility class, is used by beans that support bound properties. It manages a list of listeners and dispatches property change events to them. This class has overloaded methods: addPropertyChangeListener, removePropertyChangeListener and firePropertyChange.

2.3. The Java Bean

The bean class defines properties with getter and setter methods.

The bean uses an instance of the PropertyChangeSupport class as a member and delegates tasks to it. The bean's setter method triggers the property change event. Note that, in case the value set is same as that of the previous one, the event is not fired.

The bean wraps the PropertyChangeSupport and now one can make use of its functionality, like adding property change listeners, removing them and firing the property change event when the bean property changes.

The bean can have multiple properties and any of these can trigger change events.

3. Observer and Observable

PropertyChangeListener's implementer is in the role of an observer. The bean wrapping the PropertyChangeSupport is the observable.

How are these two associated? The listener is registered with the bean. The method addPropertyChangeListener is used to add the observer to the observable.

What happens then? Whenever there is a change in the bean property (the bean's setter method along with the firePropertyChange is invoked) the observer is notified. This runs the code associated with the listener's propertyChange() method. The method has a PropertyChangeEvent as its parameter with the property details.

3.1. Multiple Observers and Observables

An observable can have multiple observers and each is registered with it.

It is also possible, that an observer might be registered with multiple observables. This is typical with Java's Swing GUI applications. The main window can be an observer and the various dialog windows which are used for transactions send messages to the main window about updates to their respective transaction summaries, count's or an update in a table or a list.

4. The Example

The example has two Swing JFrame windows created by two classes; Frame1 and Frame2. The frame-1 creates a bean with a string property whose change is observed. Frame-1 also creates an instance of a property change listener, an observer, and this is registered with the bean, the observable.

In this application, the bean's property is changed in the second window, frame-2. When a user clicks within the frame-2, a mouse listener's mouse clicked event updates the bean property. This in turn fires a property change event and the registered observer is notified of the change; the observer in the frame-1 receives the change notification which is seen as a message in a JLabel.

GUI image 1 GUI image 2 GUI image 3

4.1. Source Code

The Bean Class:
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeListener;

public class MessageBean {

    private String message; // the bean  property
    private final PropertyChangeSupport support = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        support.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        support.removePropertyChangeListener(listener);
    }

    public String getMessage() {
        return this.message;
    }

    public void setMessage(String newValue) {
        String oldValue = this.message;
        this.message = newValue;
        // The parameter values of firePropertyChange method
        // constitute the PropertyChangeEvent object
        support.firePropertyChange("message", oldValue, newValue);
    }
}
The Two Window Classes:
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.border.EmptyBorder;
import java.awt.Font;
import java.awt.event.MouseEvent;
import java.awt.event.MouseAdapter;

public class Frame1 {

    public static void main(String [] args) {
        new Frame1();
    }
	
    public Frame1() {
	
        JFrame frame = new JFrame("Frame-1");
        final JLabel label = new JLabel("Observing...");
        label.setFont(new Font("Dialog", Font.PLAIN, 18));
        frame.add(label);
        frame.getRootPane().setBorder(new EmptyBorder(10, 10, 10, 10));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(250, 150);
        frame.setLocation(200, 200);
        frame.setVisible(true);

        // Make an instance of the bean (an observable), and
        // register a property change listener (observer) with the bean        
        MessageBean bean = new MessageBean();
        bean.addPropertyChangeListener(e -> 
            label.setText((String) e.getNewValue())
        );

        // Open the second window
        new Frame2(bean);
    }
}

class Frame2 {

    private int clicks;
    
    public Frame2(MessageBean bean) {
    
        JFrame frame = new JFrame("Frame-2");
        JLabel label = new JLabel("Click anywhere to fire a property change event");
        label.setFont(new Font("Dialog", Font.PLAIN, 18));
        frame.add(label);
        frame.getRootPane().setBorder(new EmptyBorder(10, 10, 10, 10));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setSize(450, 150);
        frame.setLocation(600, 200);
        frame.setVisible(true);
        
        // A click event on the frame triggers the bean property
        // change from this mouse listener.
        frame.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                String data = "Click-count [" + ++clicks + "]";
                // The bean's changed property notifies the registered observer
                bean.setMessage(data);
            }
        });
    }
}

5. Download

Download source code here: PropertyChangeListener.zip

Return to top