pecs原则在装饰器模式中规定:作为生产者(读取)用? extends t,作为消费者(写入)用? super t;loggingprocessor对delegate用? extends t实现安全读取,defaultingprocessor则需用? super t保障输入安全,二者不可互换。

Java 中 PECS(Producer Extends, Consumer Super)原则在装饰器模式中用于约束泛型参数类型,核心是:当装饰器需要**读取**被装饰对象的泛型值(即作为生产者),应使用 ? extends T;当装饰器需要**写入**或传入新值(即作为消费者),应使用 ? super T。这确保类型安全,同时保留装饰链的灵活性。
装饰器接口需明确角色:谁产出、谁消费
假设有一个泛型组件接口 Processor<t></t>:
interface Processor<t> {
T process(T input);
}</t>
装饰器如 LoggingProcessor<t></t> 既要接收输入(T),又要返回结果(T),此时 T 是**双向角色**,不能直接用通配符——必须保持具体类型一致性。但若你设计的是「只读增强」或「只写增强」的装饰器,则可应用 PECS:
- 若装饰器仅记录输入并委托处理(不修改输入类型),且希望接受更宽泛的输入(如所有
Number子类),则构造时对被装饰对象用Processor extends Number>—— 它是Number的生产者(返回Number或子类) - 若装饰器要注入统一的默认值(比如把
null替换为某个T实例),且希望支持向上转型写入(如向Object装饰器中塞String),则被装饰对象类型应声明为Processor super String>—— 它是String的消费者
实际装饰器类中如何体现 PECS 约束
以日志装饰器为例,它不改变处理逻辑,只观察输入输出:
class LoggingProcessor<t> implements Processor<t> {
private final Processor extends T> delegate; // ✅ delegate 生产 T 或其子类 → 安全读取
LoggingProcessor(Processor extends T> delegate) {
this.delegate = delegate;
}
@Override
public T process(T input) {
System.out.println("Before: " + input);
T result = delegate.process(input); // 可安全调用,因为 delegate 至少能处理 T 的子类,而 input 是 T
System.out.println("After: " + result);
return result;
}
}</t></t>
注意:delegate.process(input) 成立的前提是 input 类型 ≤ delegate 所支持的输入类型。由于 delegate 声明为 Processor extends T>,其 process 方法签名实际是 <s extends t> S process(S input)</s>(JLS 推导),因此传入 T 是安全的——这是 PECS 在「生产者位置」保障协变的关键。
避免反模式:在消费者位置误用 extends
错误示例(编译失败):
class DefaultingProcessor<t> implements Processor<t> {
private final T defaultValue;
private final Processor extends T> delegate; // ❌ 错误!这里需要能接收 T 的 delegate
DefaultingProcessor(T defaultValue, Processor extends T> delegate) {
this.defaultValue = defaultValue;
this.delegate = delegate;
}
@Override
public T process(T input) {
return delegate.process(input == null ? defaultValue : input); // 编译错误!
// 因为 ? extends T 表示 delegate 输入类型未知(可能是 Integer、Double…),无法保证接受 T
}
}</t></t>
正确做法是将 delegate 声明为 Processor super T>:
-
Processor super T>表示该处理器至少能处理T及其所有父类,因此传入T总是安全的 - 但返回类型变为
? super T,即可能返回Object,所以需额外类型检查或限制上界(例如搭配Function<t r></t>分离输入/输出)
组合装饰时 PECS 的链式影响
多个装饰器嵌套时,PECS 约束会逐层传导。例如:
Processor<integer> base = ...; Processor extends Number> logged = new LoggingProcessor(base); // OK:Integer ⊆ Number Processor super Integer> defaulted = new DefaultingProcessor(0, logged); // ❌ 编译失败!logged 是 ? extends Number,不是 ? super Integer </integer>
原因:PECS 不可逆。? extends Number 不能赋值给 ? super Integer,二者无包含关系。解决方法是统一用具体类型(如 Processor<integer></integer>)或引入桥接泛型方法:
<u> Processor<u> chain(Processor super U> consumer, Processor extends U> producer) {
return input -> consumer.process(producer.process(input));
}</u></u>
这种写法依赖类型推断,适用于简单组合;复杂场景建议用「类型不变」的装饰器基类 + 显式类型参数,而非过度依赖通配符。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











