pageimpl 构造函数体现 pecs 原则:content 参数声明为 list

Spring Data 的 PageImpl<t></t> 构造函数是 PECS(Producer Extends, Consumer Super)原则在真实框架代码中非常典型的体现,尤其体现在其对内容列表(content)参数的泛型设计上。
PageImpl 构造函数明确使用 extends T> 接收 content
查看 PageImpl 的核心构造方法:
public PageImpl(List extends T> content, Pageable pageable, long total) { ... }
这里 List extends T> 表明:content 是一个只读的数据生产者。它不负责写入新元素,只提供已有的、类型兼容的元素供外部消费(比如遍历、序列化、分页展示)。这完全符合 PECS 中 “Producer → extends” 的逻辑。
- 你可以传入
List<user></user>构造PageImpl<person></person>(假设User extends Person) - 也可以传入
List<admin></admin>构造PageImpl<role></role>(若Admin extends Role) - 但不能往这个
content列表里add(new Person())——编译器会拒绝,因为通配符上限不确定
为什么不用 List?——灵活性与安全性的权衡
如果构造函数写成 List<t> content</t>,调用方就必须严格匹配类型:
- 无法把
List<customer></customer>传给PageImpl<user></user>,哪怕Customer是User的子类 - 导致大量不必要的类型转换或包装操作,破坏泛型初衷
而 ? extends T 在不牺牲类型安全的前提下,允许更宽泛的输入,让 PageImpl 成为真正可复用的“结果容器”,而非强绑定的具体实现。
对比:Page 接口中的 getContent() 方法也遵循同一逻辑
Page<t></t> 接口定义:
List<t> getContent();</t>
注意:返回的是 List<t></t>,不是 List extends T>。这是因为接口使用者需要确信拿到的是 T 类型元素(比如用于流式处理、DTO 转换),而 PageImpl 内部通过构造时的 ? extends T 输入 + 运行时类型擦除保障了这一契约——你放进来的都是 T 或其子类,取出来自然能安全转为 T。
这种“输入宽松(extends)、输出严谨(T)”的设计,正是 PECS 在分层 API 中的自然延伸。
延伸:PageImpl 不暴露写入能力,印证其“纯生产者”角色
PageImpl 没有提供 add()、set() 等修改 content 的方法。它的职责就是封装、呈现已有数据。这也从行为层面印证了 ? extends T 的合理性——既然不消费(写入)T,就不该用 ? super T;既然只产出(读取)T,就该用 ? extends T。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











