Java泛型观察者模式通过将事件类型作为泛型参数绑定到Event、Observer和Subject,实现类型安全的事件监听,避免强制转换。具体包括:泛型Event类封装事件数据;Observer<E>接口约束监听事件类型;Subject<E>专注单一事件类型并管理对应观察者列表;使用时类型自动对齐,编译期校验。

Java 中用泛型实现观察者模式,核心是让观察者能监听任意类型事件,同时避免强制类型转换、提升类型安全。关键在于把事件类型作为泛型参数绑定到观察者和被观察者上。
定义泛型事件类
事件对象本身应是泛型的,便于携带任意数据:
- 不继承任何框架类,保持轻量;
- 可包含时间戳、来源对象、业务数据等通用字段;
- 泛型参数 E 表示具体事件内容类型。
public class Event<E> {
private final long timestamp;
private final Object source;
private final E data;
public Event(Object source, E data) {
this.timestamp = System.currentTimeMillis();
this.source = source;
this.data = data;
}
public long getTimestamp() { return timestamp; }
public Object getSource() { return source; }
public E getData() { return data; }
}
泛型观察者接口
观察者只关心自己能处理的事件类型,用泛型约束 onEvent 方法参数:
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 接口声明为 Observer<E>,E 即它期望接收的事件数据类型;
- 避免在实现类中做
(MyEvent) event.getData()这类手动转型; - 一个观察者可监听多种事件,就需多个泛型实现(如
Observer<UserLoginEvent>和Observer<OrderPaidEvent>)。
public interface Observer<E> {
void onEvent(Event<E> event);
}
泛型被观察者(事件发布器)
被观察者内部维护不同类型事件的观察者集合,按事件类型分组管理:
立即学习“Java免费学习笔记(深入)”;
- 用 Map<Class<?>, List<Observer<?>>> 存储,但注意通配符带来的类型擦除问题;
- 更稳妥做法是用 Map<Class<?>, List> + 运行时类型检查,或引入类型令牌(TypeToken);
- 推荐简化版:每个被观察者专注一种事件类型,声明为 Subject<E>,内部只存
List<Observer<E>>; - 触发通知时,直接遍历同类型观察者,编译期保证类型匹配。
public class Subject<E> {
private final List<Observer<E>> observers = new ArrayList<>();
public void addObserver(Observer<E> observer) {
observers.add(observer);
}
public void removeObserver(Observer<E> observer) {
observers.remove(observer);
}
public void notifyObservers(E data) {
Event<E> event = new Event<>(this, data);
observers.forEach(observer -> observer.onEvent(event));
}
}
使用示例:用户登录事件监听
定义具体事件类型与观察者,类型自动对齐:
-
UserLoginEvent是普通 POJO; -
LoginLogger实现Observer<UserLoginEvent>,方法参数天然就是UserLoginEvent; - 注册和触发都无需 cast,IDE 能提示、编译器会校验。
public class UserLoginEvent {
private final String username;
public UserLoginEvent(String username) { this.username = username; }
// getter...
}
public class LoginLogger implements Observer<UserLoginEvent> {
@Override
public void onEvent(Event<UserLoginEvent> event) {
System.out.println("User logged in: " + event.getData().getUsername());
}
}
// 使用
Subject<UserLoginEvent> loginSubject = new Subject<>();
loginSubject.addObserver(new LoginLogger());
loginSubject.notifyObservers(new UserLoginEvent("alice"));

















