Observable.notifyObservers(): примеры (JAVA)

Обзор метода notifyObservers класса Observable
Раздел: Наблюдение
Observable.notifyObservers(): void

Описание Observable.notifyObservers()

Метод notifyObservers() входит в класс java.util.Observable и предназначен для оповещения всех зарегистрированных наблюдателей (реализаций java.util.Observer) о том, что состояние наблюдаемого объекта изменилось. Существует две перегрузки: notifyObservers() и notifyObservers(Object arg). Метод ничего не возвращает (тип void).

Ключевые моменты поведения:

  • Оповещение происходит только если у Observable установлен флаг изменения (функция setChanged() была вызвана). После оповещения флаг автоматически сбрасывается (внутри notifyObservers используется clearChanged()).
  • При вызове notifyObservers без аргумента каждому наблюдателю передаётся null в качестве второго параметра метода update(Observable o, Object arg). При использовании notifyObservers(arg) передаётся именно этот объект.
  • Метод синхронизован по внутреннему состоянию Observable, чтобы список наблюдателей и флаг changed обрабатывались корректно в многопоточной среде. При оповещении создаётся копия списка наблюдателей, и дальше вызовы update выполняются вне критической секции для уменьшения вероятности взаимных блокировок.
  • Класс java.util.Observable и интерфейс java.util.Observer помечены как устаревшие (deprecated) начиная с Java 9. В новых проектах рекомендуется использовать альтернативные механизмы реактивного или событийного взаимодействия.

Подписи методов (ключевые):

  • public void notifyObservers()
  • public void notifyObservers(Object arg)
  • protected void setChanged() - устанавливает флаг изменения, необходимое предварительное действие для оповещения.
  • protected void clearChanged() - сбрасывает флаг; обычно вызывается внутри notifyObservers.

Аргументы и возвращаемые значения:

  • Аргумент: либо отсутствует, либо произвольный объект Object arg, который будет передан наблюдателям. Значение может быть null.
  • Возврат: отсутствует (void).

Примеры использования notifyObservers()

Пример 1. Минимальный рабочий пример с передачей аргумента.

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

class MyObservable extends Observable {}

public class Demo1 {
    public static void main(String[] args) {
        MyObservable obs = new MyObservable();

        obs.addObserver(new Observer() {
            @Override
            public void update(Observable o, Object arg) {
                System.out.println("Observer received: " + arg);
            }
        });

        obs.setChanged();
        obs.notifyObservers("Hello");
    }
}
Observer received: Hello

Пример 2. Вызов notifyObservers() без setChanged() - оповещение не произойдёт.

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

class MyObservable2 extends Observable {}

public class Demo2 {
    public static void main(String[] args) {
        MyObservable2 obs = new MyObservable2();

        obs.addObserver((o, arg) -> System.out.println("Update: " + arg));

        //Без setChanged()
        obs.notifyObservers("Will not be delivered");

        obs.setChanged();
        obs.notifyObservers("Delivered");
    }
}
Delivered

Пример 3. Несколько наблюдателей и null-аргумент.

MyObservable obs = new MyObservable();
obs.addObserver((o, arg) -> System.out.println("A: " + arg));
obs.addObserver((o, arg) -> System.out.println("B: " + arg));
obs.setChanged();
obs.notifyObservers();
A: null
B: null

Альтернативы и похожие механизмы в Java

  • java.beans.PropertyChangeSupport - для отслеживания изменений конкретных свойств объекта. Предпочтителен при необходимости передачи имени свойства, старого и нового значений. Не помечен как устаревший.
  • java.util.concurrent.Flow (Reactive Streams API) - асинхронная, back-pressure-aware модель для потоковой передачи данных. Подходит для реактивных сценариев и больших объёмов событий.
  • SubmissionPublisher - реализация издателя из Flow API, удобна для простых публикаций с возможностью асинхронной доставки.
  • Сторонние библиотеки (RxJava, Project Reactor) - расширенные реактивные возможности, операторы преобразования, объединения и т.д.
  • События на базе слушателей (Listener pattern) - пользовательские интерфейсы слушателей и методы add/removeListener; часто применяется в GUI и серверных компонентах.

Выбор зависит от требований: для простых уведомлений PropertyChangeSupport чаще удобнее, для реактивной обработки данных - Flow или RxJava.

Эквиваленты в других языках

Краткие примеры и отличия по языкам.

  • PHP - встроенные интерфейсы SplSubject/SplObserver: уведомления синхронные, похожие на Observable, но не помечены устаревшими.
  • <?
    class MySubject implements SplSubject {
        private $observers;
        private $state;
        public function __construct() { $this->observers = new SplObjectStorage(); }
        public function attach(SplObserver $o){ $this->observers->attach($o); }
        public function detach(SplObserver $o){ $this->observers->detach($o); }
        public function notify(){
            foreach ($this->observers as $obs) { $obs->update($this); }
        }
        public function setState($s){ $this->state = $s; }
        public function getState(){ return $this->state; }
    }
    class MyObserver implements SplObserver {
        public function update(SplSubject $s){ echo "State: " . $s->getState() . "\n"; }
    }
    $sub = new MySubject();
    $sub->attach(new MyObserver());
    $sub->setState('ok');
    $sub->notify();
    ?>
    State: ok
  • JavaScript - EventEmitter (Node.js) или RxJS Observable. EventEmitter синхронен и простой; RxJS предоставляет богатый набор операторов и асинхронность.
  • // Node.js
    const EventEmitter = require('events');
    const ev = new EventEmitter();
    ev.on('m', (data) => console.log('got', data));
    ev.emit('m', 5);
    got 5
  • Python - нет стандартного Observable; часто создаётся собственная реализация или используются библиотеки (RxPy). Простейшая реализация похожа на Java:
  • class Observable:
        def __init__(self):
            self._obs = []
            self._changed = False
        def add_observer(self, o): self._obs.append(o)
        def set_changed(self): self._changed = True
        def notify(self, arg=None):
            if not self._changed: return
            for o in list(self._obs): o.update(self, arg)
            self._changed = False
    
    class Obs:
        def update(self, source, arg):
            print('got', arg)
    
    o=Observable(); o.add_observer(Obs()); o.set_changed(); o.notify('x')
    got x
  • C# - встроенные события и делегаты (event) и интерфейсы IObservable/IObserver. События проще для случаев publish/subscribe; Reactive Extensions даёт расширенные возможности.
  • using System;
    class Program {
      static event Action OnMsg;
      static void Main(){
        OnMsg += s => Console.WriteLine(s);
        OnMsg?.Invoke("hi");
      }
    }
    
    hi
  • Go - обычно используются каналы (channels) для передачи событий между горутинами; паттерн observer реализуется через подписку на канал.
  • package main
    import "fmt"
    func main(){
        ch := make(chan string)
        go func(){
            for m := range ch { fmt.Println(m) }
        }()
        ch <- "msg"
        close(ch)
    }
    
    msg
  • Kotlin - похожие подходы: Delegates.observable для отслеживания изменений свойства, Flow/Channel для реактивных сценариев и LiveData в Android для lifecycle-aware подписки.
  • import kotlin.properties.Delegates
    var p: Int by Delegates.observable(0) { prop, old, new ->
        println("$old -> $new")
    }
    fun main(){ p = 5 }
    
    0 -> 5

Отличия от Java Observable: в языках обычно есть более современные или встроенные механизмы событий (делегаты, каналы, Rx-подходы), и они чаще не помечены устаревшими.

Типичные ошибки при использовании notifyObservers()

  • Отсутствие setChanged() - самый распространённый промах; notifyObservers не оповестит наблюдателей, если флаг изменения не установлен. Пример выше (Demo2) иллюстрирует это.
  • Ожидание возврата значения - notifyObservers возвращает void; для получения ответов от наблюдателей требуется собственный протокол (например, callback-схема или Future).
  • Изменение списка наблюдателей во время оповещения - прямые модификации списка наблюдателей внутри update могут приводить к неожиданному поведению; Observable создаёт копию списка перед вызовами, но кастомные реализации могут быть уязвимы.
  • Передача изменяемых объектов - если передаётся мутабельный объект, все наблюдатели получают ссылку на один экземпляр; изменение аргумента в одном наблюдателе повлияет на остальных.
  • Многопоточность - хотя Observable синхронизирован, логика в update может блокировать; долгие операции в update блокируют поток уведомления. В таких случаях стоит делегировать обработку в отдельную задачу.
  • Использование устаревшей API - Observable/Observer помечены deprecated; начинать новый проект с них не рекомендуется.

Пример ошибки: ожидание результата от наблюдателя.

// Ожидание значения (не сработает на базе notifyObservers)
// Не существует стандартного механизма возврата результата через notifyObservers()
(нет вывода; необходимость собственной схемы ответов)

Изменения и статус API

  • Класс java.util.Observable и интерфейс java.util.Observer помечены как устаревшие (deprecated) в Java 9. Причина: ограниченная гибкость, синхронизированный подход и плохая интеграция с современными реактивными моделями.
  • Никаких существенных изменений в поведении notifyObservers в поздних версиях Java не вводилось; вместо этого рекомендовано использовать современные альтернативы: Flow API, SubmissionPublisher, PropertyChangeSupport или сторонние реактивные библиотеки.
  • Для новых проектов рекомендуется выбирать API, соответствующий требованиям: синхронные простые уведомления - PropertyChangeSupport или пользовательские listener'ы; асинхронные и потоковые сценарии - Flow/Rx.

Расширенные и редкие варианты применения

Пример A. Асинхронная доставка уведомлений за пределы notifyObservers: делегирование выполнения в отдельный Executor, чтобы не блокировать поток, вызывающий notifyObservers.

Пример java
import java.util.*;
import java.util.concurrent.*;

class AsyncObservable extends Observable {
    private final Executor exec = Executors.newCachedThreadPool();
    public void notifyObserversAsync(Object arg) {
        if (!hasChanged()) return;
        List obs;
        synchronized (this) {
            obs = new ArrayList<>(this.countObservers()>0 ? Collections.list(new Vector<>(Arrays.asList()).elements()) : Collections.emptyList());
            //Прямой доступ к списку наблюдателей в Observable защищён, но для демонстрации используется копирование.
            clearChanged();
        }
        // в реальном коде следует получить копию через API или переопределить хранение наблюдателей
        for (Observer o : obs) {
            exec.submit(() -> o.update(this, arg));
        }
    }
}

// Использование ниже опущено из-за громоздкости примера
(выполнение update в пуле потоков; вывод зависит от реализации наблюдателей)

Пример B. Миграция с Observable на PropertyChangeSupport для отслеживания конкретного свойства.

Пример java
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class BeanWithPCS {
    private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
    private int value;
    public void addListener(PropertyChangeListener l){ pcs.addPropertyChangeListener(l); }
    public void removeListener(PropertyChangeListener l){ pcs.removePropertyChangeListener(l); }
    public void setValue(int v){
        int old = this.value; this.value = v;
        pcs.firePropertyChange("value", old, v);
    }
}

// При добавлении слушателя метод update будет получать старое и новое значение
value изменилось: old -> new (в зависимости от слушателя)

Пример C. Кастомная реализация с возвратом результатов от наблюдателей: сбор ответов и объединение.

Пример java
import java.util.*;

interface ResObserver { Object update(Object arg); }

class ResObservable {
    private final List obs = new ArrayList<>();
    void add(ResObserver o){ obs.add(o); }
    List notifyAndCollect(Object arg){
        List results = new ArrayList<>();
        for (ResObserver o : new ArrayList<>(obs)) {
            results.add(o.update(arg));
        }
        return results;
    }
}

// Использование:
// ResObservable r = new ResObservable();
// r.add(a -> "ok:" + a);
// var res = r.notifyAndCollect("x");

["ok:x"]

Пример D. Использование Flow API как современная альтернатива для back-pressure-aware передачи событий.

Пример java
import java.util.concurrent.SubmissionPublisher;

public class FlowDemo {
    public static void main(String[] args) throws Exception {
        try (SubmissionPublisher pub = new SubmissionPublisher<>()) {
            pub.subscribe(new java.util.concurrent.Flow.Subscriber<>() {
                private java.util.concurrent.Flow.Subscription sub;
                public void onSubscribe(java.util.concurrent.Flow.Subscription s){ sub = s; s.request(1); }
                public void onNext(String item){ System.out.println("got: " + item); sub.request(1); }
                public void onError(Throwable t){}
                public void onComplete(){}
            });
            pub.submit("a");
            Thread.sleep(100);
        }
    }
}
got: a

Комментарии к расширенным вариантам:

  • Асинхронные вызовы обеспечивают немедленное возвращение инициатору, но требуют обработки ошибок и синхронизации результатов.
  • PropertyChangeSupport удобен при необходимости передачи старого и нового значений свойства.
  • Flow API и Rx-подходы обеспечивают гибкое управление скоростью передачи и обработкой ошибок.

джава Observable.notifyObservers() function comments

En
Observable.notifyObservers() Оповещает всех наблюдателей