泛型通配符是支撑解耦的关键语法工具:? extends T 实现只读契约,隔离生产方;? super T 支持可扩展写入,松绑消费者;结合泛型接口与类型令牌,构建可插拔能力契约并规避擦除盲区。

泛型通配符本身不直接“实现解耦”,但它在架构设计中是支撑解耦的关键语法工具——尤其配合PECS原则和面向接口编程时,能显著降低模块间对具体类型的强依赖。
用 ? extends T 实现只读契约,隔离数据生产方
当一个组件只负责提供数据(比如配置中心、缓存读取器、DTO转换器),它应返回 List<? extends Product> 而非 List<Product> 或更具体的 List<Book>。这样调用方只需关心“能安全读出 Product 及其子类”,无需知道底层是内存列表、数据库查询结果还是远程API响应。
- 上游模块(如 Repository)返回
List<? extends Entity>,下游 Service 不依赖具体实体类型 - 避免暴露实现细节:返回
Collection<? extends Command>,命令调度器无需知道是SaveCommand还是DeleteCommand - 禁止意外写入:编译器阻止向
? extends T集合添加元素(除 null),天然防止污染数据源
用 ? super T 实现可扩展写入,松绑消费者接口
当一个模块负责接收并处理数据(如日志收集器、事件总线、批量导入器),它应接受 List<? super Event>。这意味着它可以安全接收 Event、SystemEvent、甚至 Object 类型的容器——只要保证写入的是 Event 或其子类。
- 通用写入方法签名:
void enqueueAll(Queue<? super Message> queue, List<? extends Message> messages),两端都解耦 - 框架层(如 Spring Messaging)用
MessageHandler<? super Payload>允许用户注册处理任意子类型处理器 - 写入自由 + 读取受限:从
? super T读出的元素只能当Object处理,强制调用方做显式转型或使用泛型方法封装,把类型决策上移到更合适的层级
结合泛型接口与通配符,构建可插拔能力契约
定义能力接口时不绑定具体类型,而是用通配符声明输入/输出边界。例如:
立即学习“Java免费学习笔记(深入)”;
-
interface DataTransformer<IN, OUT> { OUT transform(IN input); }→ 看似泛型,但若固定为DataTransformer<String, Integer>就锁死了用途 - 改为:
<T> void registerTransformer(Transformer<? super T, ? extends Result> t),注册时由使用者决定 T 是JsonNode还是byte[] - 配合工厂模式:
ConverterFactory<? super Source, ? extends Target>的实现类可被自动发现并注入,不依赖具体 source/target 组合
规避擦除带来的运行时盲区,用类型令牌补足契约信息
由于泛型擦除,List<? extends Number> 在运行时退化为 List,无法反射获取上界。架构中需主动保留关键类型线索:
- 传入
TypeReference<List<User>>(Jackson 风格)替代原始List参数,让反序列化器知道目标结构 - 定义
TypedHandler<T>接口,并要求实现类提供Class<T> getType()方法,供路由层匹配 - 在注解中嵌入泛型意图:
@HandlerFor(type = User.class, subtypes = true),比硬编码if (obj instanceof User)更易维护


















