How does the JavaBeans PropertyChangeSupport / PropertyChangeListener mechanism implement the Observer pattern, and how do you use it correctly?
answer
- Compose a PropertyChangeSupport(this) field
- Listener = PropertyChangeListener.propertyChange(evt)
- Setter: capture old, set field, firePropertyChange(name, old, new)
- Skips firing when old.equals(new)
- Synchronous, registration order, can scope to one property
basics
~20 sA bean keeps a PropertyChangeSupport helper object. Listeners register with addPropertyChangeListener. When a property changes, the bean calls firePropertyChange(name, oldValue, newValue), and every listener's propertyChange method runs with a PropertyChangeEvent. It only fires when old and new values actually differ.
solid answer
~40 sThe java.beans package gives a composition-based Observer implementation. Your bean holds a `PropertyChangeSupport` instance (usually `final`, constructed with `this` as the source). Listeners implement `PropertyChangeListener` (one method, `propertyChange(PropertyChangeEvent)`) and register via `addPropertyChangeListener`, optionally scoped to a named property. In each setter you fire `support.firePropertyChange("name", oldValue, newValue)` — crucially *after* updating the field, capturing the old value first. `firePropertyChange` skips notification when old equals new (using equals), preventing spurious events. The `PropertyChangeEvent` carries the source, property name, old and new values, so listeners are typed and self-describing. Because you *compose* the support object rather than extend a base class, your bean is free to extend anything else. Notification is synchronous on the calling thread and listeners fire in registration order. This is the idiomatic single-JVM replacement for the deprecated java.util.Observable.
code
java · 23 linespublic class Account {
private final PropertyChangeSupport support = new PropertyChangeSupport(this);
private int balance;
public void addPropertyChangeListener(PropertyChangeListener l) {
support.addPropertyChangeListener(l);
}
public void removePropertyChangeListener(PropertyChangeListener l) {
support.removePropertyChangeListener(l);
}
public void setBalance(int newBalance) {
int old = this.balance; // capture old first
this.balance = newBalance; // mutate
support.firePropertyChange("balance", old, newBalance); // skipped if old == new
}
}
// usage
Account a = new Account();
a.addPropertyChangeListener(evt ->
System.out.println(evt.getPropertyName() + ": " + evt.getOldValue() + " -> " + evt.getNewValue()));
a.setBalance(100); // prints "balance: 0 -> 100"go deeper
Knows there is a listener interface and a support object, and that a bean fires events when a property changes.
Can write a bean that composes PropertyChangeSupport, exposes add/remove, and fires correctly from setters with old/new values.
Explains synchronous-on-calling-thread + registration-order semantics, equals-based no-op suppression, per-property subscriptions, and the composition-over-inheritance advantage over Observable.
Discusses when this synchronous model is appropriate versus Flow's async backpressure, thread-marshalling concerns for UI listeners, and patterns for testing/observing property change graphs at scale.
## The problem this solves You have an object (a **bean** — just a plain Java object with properties and getters/setters) and you want other code to be notified whenever one of its properties changes, without the bean knowing who is listening. That is the Observer pattern. The JDK's modern, idiomatic answer for synchronous, in-process property notifications lives in the **`java.beans`** package. ## The three pieces 1. **`PropertyChangeListener`** — an interface with a single method `void propertyChange(PropertyChangeEvent evt)`. This is the *observer*. Because it has one abstract method it is a functional interface, so you can implement it with a lambda. 2. **`PropertyChangeEvent`** — the event object passed to the listener. It exposes `getSource()` (the bean), `getPropertyName()`, `getOldValue()`, and `getNewValue()`. Values are typed as `Object` but are well-described. 3. **`PropertyChangeSupport`** — a ready-made helper that manages the listener list and does the firing. The key design choice: you **compose** it (hold it as a field) rather than extend it. Construct it as `new PropertyChangeSupport(this)` so events report the bean as their source. ## How you wire it up In the bean you expose `addPropertyChangeListener` / `removePropertyChangeListener` that delegate to the support object. In each property setter you: 1. capture the **old value** before mutating; 2. set the field; 3. call `support.firePropertyChange(propertyName, oldValue, newValue)`. `firePropertyChange` compares old and new with `equals` (and handles nulls) and **does nothing if they are equal**, which avoids notifying listeners about no-op writes. You can also register a listener for *one named property* via `addPropertyChangeListener(String propertyName, listener)`; that listener only hears about that property. ## Threading and ordering Notification is **synchronous**: `firePropertyChange` invokes each listener on the calling thread, in registration order, before returning. There is no built-in asynchrony or backpressure — if you need those, that is what `java.util.concurrent.Flow` is for. `PropertyChangeSupport` is thread-safe for add/remove/fire (it copies the listener array), but your listeners run on whatever thread fired the change, so a UI listener may need to marshal back to the event thread. ## Why this beats java.util.Observable - **Composition over inheritance** — your bean stays free to extend any class. - **Typed, self-describing events** — name + old + new value, no `setChanged()` ceremony. - **No-op suppression** — equal old/new is skipped automatically. - **Per-property subscriptions** — listeners can target a single property. ## Deriving your own answer A middle engineer should be able to describe the three types and write a correct setter that fires after capturing the old value. A senior adds the synchronous/registration-order semantics, the equals-based suppression, and the composition rationale versus Observable.
- Why must you capture the old value before mutating the field?firePropertyChange needs the genuine previous value to (a) include it in the event and (b) suppress the notification when old equals new. If you read the field after mutating, old and new are identical and either no event fires or the event is wrong.
- How would you let a listener subscribe to only one property?Use the overload addPropertyChangeListener(String propertyName, PropertyChangeListener l). That listener is only invoked for events whose property name matches; PropertyChangeSupport routes named subscriptions accordingly.
saying these in an interview costs you the question
- Extending PropertyChangeSupport instead of composing it
- Firing before updating the field, or passing the same value as old and new
- Assuming firing is asynchronous or provides backpressure
- Believing it always notifies even when the value did not change