QueryWrapper<T> 是 T 的消费者而非生产者,其泛型 T 用于接收字段元数据和条件值,生成 SQL,不产出 T 实例,语义上等价于 Consumer<T>。

QueryWrapper 本身不直接体现 Java 泛型中的 PECS(Producer Extends, Consumer Super)原则,因为它不是以泛型容器或数据生产/消费角色设计的工具类。它的字段比较方法(如 eq("name", "张三") 或 eq(User::getName, "张三"))本质是条件构造行为,而非对泛型集合的读写操作。
但若从其泛型参数 <T> 的使用方式切入,可以清晰看到它严格遵循 Consumer Super 的逻辑——即:QueryWrapper<T> 是一个 T 的消费者(Consumer),它接收 T 类型的实例信息(字段名、值、条件逻辑),不向外产出 T 实例。
H3:QueryWrapper 的泛型 T 是“被消费”的类型
QueryWrapper<User> 中的 User 不代表它会返回 User 对象,而是说明:
- 所有字段引用(如
"name"或User::getName)都应属于User类 - 所有条件值(如
"张三"、18)将用于生成 WHERE 子句,作用于User映射的表 - 它消费关于
User的元数据和业务值,不生产User实例
这正符合 PECS 中的 Consumer Super 精神:当一个泛型类型主要用于接受/处理某类数据时,应使用 super T —— 虽然 QueryWrapper 声明为 <T>(非 <? super T>),但其设计语义与 Consumer<T> 高度一致。
立即学习“Java免费学习笔记(深入)”;
✅ 正确理解:
QueryWrapper<User>≈Consumer<User>(语义上)
❌ 错误理解:QueryWrapper<User>是User的生产者或持有者
H3:LambdaQueryWrapper 更凸显 Consumer 特性
LambdaQueryWrapper<User> 的字段方法必须传入 User 的方法引用,例如:
wrapper.eq(User::getName, "张三") // 必须是 User 的 getter
.ge(User::getAge, 18)
.isNotNull(User::getEmail);编译器强制要求这些引用属于 User 或其父类(因方法引用可向上转型),但实际中几乎总是 User::xxx。这反映:
- 它在消费
User的结构信息(字段定义) - 不关心子类新增字段(不支持
UserSub::getRank,除非显式声明LambdaQueryWrapper<UserSub>) - 无法安全地接受
? super User,因为字段名需精确匹配实体,不是协变场景
所以它虽未用 super 声明,但行为上是典型的 type-safe consumer。
H3:对比真正的 PECS 应用场景(加深理解)
| 场景 | 泛型用法 | 与 QueryWrapper 类比 |
|---|---|---|
List<? extends Number>(Producer) |
只能 get() → 安全产出 Number
|
QueryWrapper 不产出 T,不适用 |
List<? super Integer>(Consumer) |
只能 add(Integer) → 安全消费 Integer
|
QueryWrapper<T> 接收 T 的字段和值,是同构逻辑 |
Function<? super User, String> |
接受 User 或其父类,输出 String
|
QueryWrapper 的字段方法类似:接受 User 结构,输出 SQL 片段 |
QueryWrapper<T> 的核心动作(eq, like, between)都是向内部条件栈“添加输入”,没有 get()、stream()、iterator() 等产出 T 的行为——纯消费。
H3:为什么不用 <? super T> 声明?
QueryWrapper<T> 声明为 <T> 而非 <? super T>,是出于实用性和 API 清晰性考虑:
- 需要
T来做反射/泛型擦除后的字段解析(如User::getName→"name") - 方法签名需绑定具体实体,便于 IDE 自动补全和编译检查
-
super会放宽类型,反而削弱字段校验能力(例如允许Object::toString,但无意义)
所以它是语义上的 Consumer,语法上保持 <T> —— 这种设计在框架 API 中很常见(如 Collections.sort(List<T>, Comparator<? super T>) 中 Comparator 才显式用 super)。
QueryWrapper 不是泛型集合,但它对 <T> 的使用完全站在“消费者”立场:读取 T 的结构、接收 T 的字段值、生成面向 T 表的 SQL。理解这一点,就能避开“以为它产出 T”或“试图用子类泛型混用字段”的典型误用。


















