泛型与通配符是旧系统向强类型架构迁移中的关键工程决策,需按“怎么配、在哪配、配到哪一层”精准落地:明确边界(如只读用? extends t、只写用? super t)、分层嵌套(适配层收敛、核心域禁用?)、规避擦除(用typetoken补全运行时信息),并严格验证接口/实现、方法调用、第三方库三处泛型一致性。

旧系统向强类型架构迁移时,泛型与通配符不是“加不加”的选择题,而是“怎么配、在哪配、配到哪一层”的工程决策问题。盲目套用 <t></t> 或堆砌 ? extends 往往让编译通过但运行出错,关键在于把类型约束落在真正需要解耦或兼容的位置。
明确泛型边界:从“宽泛声明”转向“契约驱动”
老系统中常见 List 或 Map 无类型声明,迁移到强类型时不能直接改成 List<object></object>——这等于没改。应依据实际使用场景反推上界或下界:
- 若只读取数据(如遍历展示),用
List extends Product>,允许传入List<book></book>或List<electronics></electronics> - 若只写入数据(如批量入库),用
List super Order>,支持接收List<transaction></transaction>(假设Order是其子类) - 若需读写兼顾(如缓存操作),则必须使用具体类型参数,例如
Cache<string user></string>,而非Cache, ?>
通配符分层嵌套:在API网关/适配层做收敛,不在核心域暴露
过渡期常需同时对接旧版 XML 接口(返回 Map<string object></string>)和新版 JSON 接口(返回 Response<user></user>)。此时应在适配器层封装通配符逻辑,而非让领域服务感知:
- 定义统一响应包装:
ApiResponse<t></t>,其中T在调用方确定,如ApiResponse<list extends baseentity>></list> - XML 解析器内部可使用
Map<string></string>处理任意键值,但对外只暴露toDomainObject()方法,返回强类型实例 - 禁止在 DAO 层或实体类字段中出现
?—— 这会污染整个调用链,导致类型信息在编译期丢失
泛型擦除规避:用类型令牌(TypeToken)补全运行时泛型信息
Java 泛型擦除会导致 new ArrayList<string>().getClass() == new ArrayList<integer>().getClass()</integer></string>,这对依赖反射的旧框架(如某些 RPC 序列化器)是致命伤。过渡期可引入 TypeToken 显式传递类型:
- 将
jsonParser.fromJson(json, new TypeToken<list>>() {}.getType())</list>替代fromJson(json, List.class) - 在 Spring 的
RestTemplate调用中,用ParameterizedTypeReference<list>></list>替代原始List.class - 自定义泛型工具类时,构造函数接收
Class<t></t>并缓存,用于后续instanceof判断或数组创建(如Array.newInstance(componentType, size))
迁移检查清单:三处必须验证的泛型一致性
每次合并泛型改造代码前,检查以下三点是否同步更新:
-
接口定义与实现类:若
Service<t></t>声明了泛型方法,其实现类必须显式继承ServiceImpl<user></user>,不能留空ServiceImpl -
泛型方法与调用上下文:避免
<t> T convert(Object src)</t>这类无约束泛型方法;应改为<t extends dto> T convert(Object src, Class<t> target)</t></t> -
第三方库泛型兼容性:检查 Jackson、MyBatis、Lombok 等是否支持目标 JDK 版本下的泛型推导(如 Lombok 的
@Data对泛型字段生成 getter/setter 是否完整)










