泛型方法替代多版本重载的核心是将类型差异收口至编译期检查,通过接口约束(如baserulecontext)、泛型工厂与显式class传参,实现预处理逻辑一次写对、处处复用,避免重复校验与运行时分支。

用泛型方法替代多版本重载,核心不是“删代码”,而是把类型差异收口到编译期检查,让预处理逻辑一次写对、处处复用。在规则引擎的预处理层(比如输入校验、参数标准化、上下文封装),这种替换尤其高效——因为不同业务线传入的数据结构千差万别,但处理意图高度一致:校验→转换→包装→传递。
预处理层典型重载痛点
假设规则引擎入口需支持多种原始输入:
-
订单风控场景:传入
OrderRequest -
用户注册场景:传入
UserRegisterDTO -
营销活动场景:传入
PromotionEvent
若用传统重载,会写出三个几乎一模一样的方法体,仅参数类型不同,且每个方法都要单独做非空校验、时间戳补全、traceId注入等重复动作。
泛型预处理方法定义与约束
定义一个带边界约束的泛型方法,强制所有入参实现统一契约:
public <t extends baserulecontext> RuleContext wrapAndValidate(T input) {
if (input == null) {
throw new IllegalArgumentException("input cannot be null");
}
// 公共预处理:注入traceId、补默认时间、校验基础字段
input.setTraceId(Tracer.currentTraceId());
input.setTimestamp(System.currentTimeMillis());
validateMandatoryFields(input);
// 封装为统一上下文
return new RuleContext(input);
}</t>
关键点:
-
用接口而非具体类作为上界,如
BaseRuleContext提供setTraceId、getBizId等通用方法 - 不依赖 instanceof 或类型判断,避免运行时分支,所有逻辑基于契约调用
-
返回统一的
RuleContext,下游规则执行器只认这一个输入类型
配合泛型工厂消除构造冗余
不同业务对象初始化方式不同,可搭配泛型工厂避免重复 new:
public <t extends baserulecontext> T buildInput(Class<t> clazz, Map<string object> rawParams) {
try {
T instance = clazz.getDeclaredConstructor().newInstance();
// 用通用工具填充字段(如 BeanUtils.copyProperties)
populateFromMap(instance, rawParams);
return instance;
} catch (Exception e) {
throw new RuntimeException("Failed to build input for " + clazz, e);
}
}</string></t></t>
调用示例:
wrapAndValidate(buildInput(OrderRequest.class, params))wrapAndValidate(buildInput(UserRegisterDTO.class, params))
这样就把“类型识别→实例化→预处理→封装”四步压缩为一行链式调用,且无反射隐患(构造器明确、类型安全)。
规避泛型擦除导致的运行时类型丢失
预处理层常需根据实际类型做差异化处理(如 OrderRequest 需查订单库,UserRegisterDTO 需查用户中心)。此时不能依赖 input.getClass() 做 if-else,而应:
-
在泛型方法中显式传入 Class 对象:
<t> void process(T input, Class<t> type)</t></t> - 用 TypeReference 保留泛型信息(适用于 JSON 反序列化后仍需知道嵌套泛型)
-
将类型策略外置为 SPI 接口,按
Class>查找对应Preprocessor<t></t>实现,保持扩展性
不复杂但容易忽略











