pecs原则是解释器模式中上下文参数设计的关键契约,非语法糖:作为生产者时用? extends e保障只读安全,作为消费者时用? super r支持结果归集,混合职责须拆分接口,与解释器树递归调用协同实现类型安全。

PECS 原则在解释器模式(Interpreter)的上下文参数设计中,不是语法糖或可选优化,而是保障表达式树安全遍历与类型协作的关键契约。它直接决定上下文对象能否被不同层级的解释器复用,避免运行时 ClassCastException 或编译期拒绝调用。
上下文作为“生产者”:用 ? extends E 支持多级表达式求值
当上下文(如 EvaluationContext<t></t>)主要职责是**提供环境变量、常量或预计算结果**供各解释器节点读取时,它本质上是数据生产者。此时应将上下文参数声明为 ? extends Expression 或更具体的 ? extends ArithmeticExpr。
- 允许传入
ArithmeticExpr、LogicalExpr等子类上下文,只要它们都继承自统一抽象基类 - 解释器调用
context.getValue("x")可安全返回Number或Boolean,因为编译器保证返回类型是上界声明的子类型 - 禁止在上下文中写入新变量(如
context.put("y", new Double(1.0))),否则可能破坏子类上下文的内部约束(例如某子类只允许整数变量)
上下文作为“消费者”:用 ? super R 支持统一结果归集
当上下文承担**收集执行结果、缓存中间值或触发副作用**(如日志记录、指标上报)时,它是消费者。此时应将接收结果的容器设为 List super Result> 或 Consumer super EvalResult>。
- 可安全传入
ArrayList<object></object>、LinkedList<evalresult></evalresult>,甚至ArrayList<serializable></serializable>,只要其元素类型能容纳Result及其子类 - 解释器执行后调用
context.record(result),无论result是IntResult还是StringResult,都能被接纳 - 但若需从该容器读取,只能按
Object处理——因为实际类型可能是Object,无法保证是具体子类
混合职责需拆分,而非妥协使用通配符
一个上下文若既暴露 getVariable()(读)又暴露 setVariable()(写),说明它同时承担生产者与消费者角色。这时 PECS 原则会失效:
-
List extends T>不允许add(),List super T>不允许安全get() - 强行合并在同一参数中,要么放弃类型安全(用原始类型),要么导致调用方频繁强制转型
- 正确做法是分离接口:定义
ReadOnlyContext<t></t>和MutableContext<t></t>,分别应用? extends T和? super T
与解释器树结构协同:父节点向下传递时遵循 PECS
在递归解释过程中,父解释器向子解释器传递上下文,是典型的数据流转场景:
- 父节点调用
child.interpret(context),若子节点只读上下文,则方法签名应为interpret(EvaluationContext extends Expr> ctx) - 若子节点需注册回调或注入副作用,其方法应为
interpret(EvaluationContext super SideEffect> ctx) - 这样整个解释器链路的类型流是单向、可验证的:上游产出 → 下游消费,不依赖运行时 instanceof 判断
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











