Observable.addObserver: примеры (JAVA)
Observable.addObserver(Observer o): voidОписание метода
Метод addObserver принадлежит классу java.util.Observable и имеет сигнатуру public void addObserver(Observer o). Его задача - зарегистрировать объект, реализующий интерфейс java.util.Observer, в списке наблюдателей субъекта. После регистрации этот объект будет получать уведомления при вызове notifyObservers() при условии, что состояние субъекта помечено как изменённое через setChanged().
Аргументы и поведение:
- Observer o - обязательный аргумент. При передаче
nullметод выбрасываетNullPointerException. - Метод не возвращает значения (тип
void). - Добавление производится синхронизированно. Если тот же объект уже присутствует в списке, дубликат не добавляется.
- При вызове
notifyObservers(arg)каждый зарегистрированный наблюдатель получает вызовupdate(Observable o, Object arg), если ранее вызванsetChanged().
Дополнительные замечания:
- Класс
Observableи интерфейсObserverсчитались устаревшими и помечены как@Deprecatedначиная с Java 9. Рекомендованы более современные средства (см. раздел альтернатив). - Внутренняя реализация использует синхронизируемую коллекцию; это упрощает некоторые многопоточные сценарии, но накладывает ограничения на гибкость и расширяемость.
Короткие примеры
Пример 1. Базовая регистрация и уведомление.
import java.util.Observable;
import java.util.Observer;
class MyObservable extends Observable {}
class MyObserver implements Observer {
public void update(Observable o, Object arg) {
System.out.println("Получено: " + arg);
}
}
public class Main {
public static void main(String[] args) {
MyObservable subj = new MyObservable();
MyObserver obs = new MyObserver();
subj.addObserver(obs);
subj.setChanged();
subj.notifyObservers("Привет");
}
}
Получено: Привет
Пример 2. Попытка добавить дважды тот же объект (дубликат не добавляется).
// Используются те же классы MyObservable и MyObserver
MyObservable subj = new MyObservable();
MyObserver obs = new MyObserver();
subj.addObserver(obs);
subj.addObserver(obs); // повторная регистрация
subj.setChanged();
subj.notifyObservers("Тест");
Получено: Тест
Пример 3. Передача null приводит к исключению.
MyObservable subj = new MyObservable();
subj.addObserver(null);
Exception in thread "main" java.lang.NullPointerException at java.base/java.util.Observable.addObserver(Observable.java:...) ...
Пример 4. Забыт вызов setChanged - уведомлений не будет.
MyObservable subj = new MyObservable();
MyObserver obs = new MyObserver();
subj.addObserver(obs);
// subj.setChanged(); // забыто
subj.notifyObservers("Данные");
(нет вывода)
Альтернативы в Java
- PropertyChangeSupport / PropertyChangeListener (java.beans) - позволяет подписываться на изменения именованных свойств с передачей старого и нового значений. Часто предпочтительнее для изменения состояния объектов модели.
- java.util.concurrent.Flow (и интерфейсы Publisher/Subscriber) - современный стандарт реактивной передачи данных в Java 9. Поддерживает управление давлением (backpressure) и асинхронность.
- Событийная модель JavaBeans и слушатели событий - набор конвенций для GUI и приложений, подходящая при необходимости типизированных событий.
- RxJava, Reactor - реактивные библиотеки с богатым набором операторов; предпочтительны для сложных асинхронных потоков данных.
- JavaFX ObservableValue и связанные API - удобны для UI-сценариев и биндинга свойств.
Выбор зависит от требований: для простых локальных уведомлений достаточно PropertyChangeSupport; для реактивной асинхронной передачи лучше Flow или сторонние реактивные библиотеки.
Альтернативы в других языках
Ниже приведены краткие аналоги observer-паттерна в различных языках с примерами.
PHP (SPL)
// PHP: SplSubject и SplObserver
class Subject 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 $o) $o->update($this);
}
public function setState($s){ $this->state = $s; }
public function getState(){ return $this->state; }
}
class Observer implements SplObserver {
public function update(SplSubject $s){ echo "PHP получено: " . $s->getState() . "\n"; }
}
$sub = new Subject();
$obs = new Observer();
$sub->attach($obs);
$sub->setState('ok');
$sub->notify();
PHP получено: ok
JavaScript (Node.js EventEmitter)
const EventEmitter = require('events');
const ee = new EventEmitter();
ee.on('msg', data => console.log('JS:', data));
ee.emit('msg', 'привет');
JS: привет
Python (простая реализация)
class Observable:
def __init__(self):
self._obs = []
def add_observer(self, o):
self._obs.append(o)
def notify(self, arg=None):
for o in list(self._obs):
o.update(self, arg)
class Observer:
def update(self, subj, arg):
print('Py received:', arg)
s = Observable()
o = Observer()
s.add_observer(o)
s.notify('hello')
Py received: hello
C# (события / IObservable)
// C# события
using System;
class Subject {
public event Action Updated;
public void Change(string v){ Updated?.Invoke(v); }
}
class Program{
static void Main(){
var s = new Subject();
s.Updated += v => Console.WriteLine("C#: " + v);
s.Change("ok");
}
}
C#: ok
Go (каналы)
package main
import "fmt"
func main(){
ch := make(chan string)
go func(){
for v := range ch { fmt.Println("Go:", v) }
}()
ch <- "hello"
close(ch)
}
Go: hello
Kotlin (Delegates.observable)
import kotlin.properties.Delegates
var prop: String by Delegates.observable("") { _, old, new ->
println("Kotlin: $old -> $new")
}
fun main(){ prop = "value" }
Kotlin: -> value
Lua (колбэки)
local observers = {}
local function addObserver(o) table.insert(observers, o) end
local function notify(v) for _,o in ipairs(observers) do o(v) end end
addObserver(function(x) print('Lua:', x) end)
notify('ok')
Lua: ok
SQL
В реляционных СУБД аналог - триггеры, которые реагируют на изменения данных. Отличие: триггеры исполняются внутри СУБД и не предоставляют прямую модель подписки в приложении.
Типичные ошибки
- Передача null. addObserver(null) приводит к
NullPointerException. Пример и результат приведены в разделе примеров. - Ожидание уведомления без setChanged. notifyObservers без предварительного setChanged не оповестит наблюдателей. Частая причина недоразумений.
- Изменение коллекции наблюдателей во время уведомления. Хотя Observable использует синхронизируемую коллекцию, логика обновления и модификации списка внутри обработчиков может вести к неожиданному поведению; предпочтительнее копировать список перед итерацией при сложных сценариях.
- Нарушение инкапсуляции. Наследование от Observable и доступ к защищённым полям может привести к хрупкому коду.
- Использование устаревшего API. Observable помечен как deprecated; его применение в новых проектах нежелательно.
Пример: забытый setChanged.
MyObservable subj = new MyObservable();
subj.addObserver(new MyObserver());
// subj.setChanged(); // забыто
subj.notifyObservers("X");
(нет вывода) - наблюдатель не вызван
Изменения и статус
Класс java.util.Observable и интерфейс java.util.Observer были помечены как @Deprecated в спецификации Java 9. Причины deprecation: ограниченная гибкость API, проблемы с поддержкой расширенных сценариев (например, управление подпиской, конкурентный доступ, типизация событий) и наличие более современных механизмов асинхронной работы и реактивного программирования.
За пределами пометки об устаревании изменений в поведении метода addObserver в самых новых релизах не происходило. Рекомендуется применять Flow API или сторонние реактивные библиотеки для новых разработок.
Расширенные примеры
1) Наблюдатель с фильтрацией событий на стороне субъекта.
import java.util.*;
class FilteredObservable extends Observable {
public void notifyIfChanged(Object arg, Observer filter) {
setChanged();
for (Observer o : Collections.list(super.countObservers()>0?null:new Vector())) {}
}
}
// Примечание: демонстрация идеи; в реальном коде лучше реализовать собственные структуры
(идея: фильтрация на стороне субъекта требует собственной реализации)
2) Асинхронное уведомление через ExecutorService.
import java.util.*;
import java.util.concurrent.*;
class AsyncObservable extends Observable {
private final ExecutorService ex = Executors.newSingleThreadExecutor();
public void notifyAsync(final Object arg) {
setChanged();
final Observer[] arr;
synchronized(this) {
arr = observers.toArray(new Observer[0]);
}
for (Observer o : arr) {
ex.submit(() -> o.update(this, arg));
}
}
}
// Использование: создаются наблюдатели, вызывается notifyAsync
(вызовы update выполняются в другом потоке; вывод в консоль появится асинхронно)
3) Слабые ссылки для предотвращения утечек памяти.
import java.lang.ref.WeakReference;
import java.util.*;
class WeakObservable {
private final List> list = new ArrayList<>();
public void addObserver(Observer o){ list.add(new WeakReference<>(o)); }
public void notifyObservers(Object arg){
Iterator> it = list.iterator();
while (it.hasNext()){
Observer o = it.next().get();
if (o == null) it.remove(); else o.update(null, arg);
}
}
}
(наблюдатели, не удерживаемые другими ссылками, будут автоматически убираться сборщиком мусора)
4) Мост между Observable и Flow.Publisher (упрощённый пример концепции).
// Идея: обёртка, которая при notify вызывает submit в SubmissionPublisher
// Полная реализация требует обработки подписок и backpressure
(в проектах с Java 9+ рекомендуется использовать Flow/SubmissionPublisher напрямую)
5) Удаление наблюдателя внутри update: демонстрация безопасного удаления.
class SelfRemovingObserver implements Observer {
private final Observable subj;
public SelfRemovingObserver(Observable s){ subj = s; }
public void update(Observable o, Object arg){
System.out.println("Удаляюсь после получения: " + arg);
subj.deleteObserver(this);
}
}
// При последовательных уведомлениях этот наблюдатель получит только первое сообщение
Удаляюсь после получения: первый (далее не вызывается)
джава Observable.addObserver function comments
- джава Observable.addObserver - аргументы и возвращаемое значение
- Функция java Observable.addObserver - описание
- Observable.addObserver - примеры
- Observable.addObserver - похожие методы на java
- Observable.addObserver на javascript, c#, python, php
- Observable.addObserver изменения java
- Примеры Observable.addObserver на джава