pecs原则是观察者模式中保障事件类型安全的底层约束:发布者用? extends event作为生产者只读分发,订阅者用? super t作为消费者安全注册,形成编译期类型安全的事件管道。

PECS 原则在观察者模式中不是“可选技巧”,而是保障事件类型安全流转的底层约束。当发布者向外推送事件、订阅者向内注册监听时,集合的读写角色天然分离——这正是 Producer Extends 和 Consumer Super 的典型战场。
事件发布者:用 ? extends Event(只读生产者)
发布者持有的事件监听器集合,本质是“向外提供通知能力”的生产者。它不往集合里新增监听器,只遍历调用每个监听器的 onEvent 方法。
- 声明应为
List extends EventHandler extends Event>>或更常见地:List<eventhandler super t>></eventhandler>(见下文),但关键在于事件源本身作为数据产出方——比如publish(Event e)方法接收的e是具体子类(LoginEvent、PaymentEvent),而通知逻辑需兼容所有能处理该事件的监听器 - 真正体现 PECS 的是事件分发时的泛型参数设计:若定义通用发布方法
<t extends event> void publish(T event)</t>,则遍历监听器时,应要求监听器能消费T或其父类——这就导向订阅端用? super T - 错误做法:把监听器列表声明为
List<eventhandler>></eventhandler>,会拒绝EventHandler<loginevent></loginevent>注册(因泛型不变性);正确做法是让注册接口接受EventHandler super LoginEvent>
事件订阅者:用 ? super Event(只写消费者)
订阅动作是典型的“数据流入”场景:外部传入一个能处理某类事件的监听器,系统将其加入内部集合。此时集合是消费者,必须能容纳该监听器及其所有子类型。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 注册方法签名应为:
void subscribe(EventHandler super T> handler),其中T是该监听器承诺处理的最具体事件类型 - 例如:
subscribe(new LoginHandler())要求LoginHandler implements EventHandler<loginevent></loginevent>,而注册方法声明<t> void subscribe(EventHandler super T> h)</t>,就能让LoginHandler安全注入到List<eventhandler>></eventhandler>或List<eventhandler>></eventhandler>中 - 反例:若注册方法写成
void subscribe(EventHandler<event> h)</event>,则无法传入EventHandler<loginevent></loginevent>(编译失败),因为EventHandler<loginevent></loginevent>不是EventHandler<event></event>的子类
事件分发链:上下游协同构成安全管道
完整事件流中,PECS 成对出现,形成类型可推导、无强转、无运行时 ClassCastException 的闭环:
- 发布者调用:
for (EventHandler super T> h : handlers) { h.onEvent(event); }—— 这里event是T类型,h声明为? super T,保证onEvent(T)可安全调用 - 监听器接口设计:
interface EventHandler<t> { void onEvent(T event); }</t>,配合注册时的? super T,使EventHandler<event></event>能接收LoginEvent,EventHandler<object></object>能接收任意事件 - 标准库印证:Spring 的
ApplicationEventMulticaster内部使用Set<applicationlistener super t>></applicationlistener>存储监听器,正是 PECS 消费者模式的工业级实践
避坑要点:别混淆“事件类型”和“监听器类型”的边界
常见误用源于搞错谁是 Producer、谁是 Consumer:
- ❌ 把监听器集合声明为
List<eventhandler extends event>></eventhandler>:这是让监听器“产出 Event”,但实际是监听器“消费 Event”,语义颠倒 - ❌ 在
publish()方法里对 event 做 instanceof 判断再分发:破坏多态,也绕过了 PECS 提供的编译期类型保障 - ✅ 正确姿势:发布者不关心监听器具体处理哪种子事件,只保证传入的
event能被handler.onEvent(...)接收;订阅者声明自己能处理的最小上界(如Event),而非最大下界
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










