Java泛型变量Class类型获取:无需传Class参数实现JSON反序列化
该需求可以实现,核心是解决Java泛型类型擦除导致运行时无法获取泛型参数类型的问题,常见可行方案如下:
方案1:构造Service实例时传入类型Class
将Class参数从read方法移到构造方法中,仅需在实例化时传一次即可,无需每次调用read方法重复传参。
实现代码:
public class GenericService<T> { private final Class<T> clazz; // 构造方法接收泛型对应的Class类型 public GenericService(Class<T> clazz) { this.clazz = clazz; } public ResponseWrapper<T> read(String json) throws Exception { T response = new ObjectMapper().readValue(json, clazz); return new ResponseWrapper<>(response); } }
调用方式:
ResponseWrapper<SomeData> response = new GenericService<>(SomeData.class).read("json value");
- 优点:实现简单,无额外反射开销,性能稳定
- 缺点:实例化时仍需要显式传入一次Class参数
方案2:通过泛型父类反射捕获泛型参数
Java反射支持获取继承关系中的父类泛型参数,利用该特性可以完全省略显式传Class的步骤,有两种使用形式:
实现基础
先改造GenericService的构造方法,自动捕获泛型类型:
public class GenericService<T> { private final Class<T> clazz; @SuppressWarnings("unchecked") public GenericService() { // 获取当前实例父类的泛型参数 Type superType = getClass().getGenericSuperclass(); if (superType instanceof ParameterizedType parameterizedType) { this.clazz = (Class<T>) parameterizedType.getActualTypeArguments()[0]; } else { throw new IllegalArgumentException("无法获取泛型类型,请确保是继承泛型父类的子类实例"); } } public ResponseWrapper<T> read(String json) throws Exception { T response = new ObjectMapper().readValue(json, clazz); return new ResponseWrapper<>(response); } }
形式A:自定义业务子类
为每个DTO类型定义对应的Service子类,指定泛型参数:
// 针对SomeData的专属Service public class SomeDataService extends GenericService<SomeData> {}
调用方式完全符合预期:
ResponseWrapper<SomeData> response = new SomeDataService().read("json value");
形式B:匿名内部类
无需单独定义子类,创建实例时加{}生成匿名子类即可捕获泛型:
ResponseWrapper<SomeData> response = new GenericService<SomeData>(){}.read("json value");
- 优点:完全不需要显式传入Class参数,符合期望的调用形式
- 缺点:形式A需要为每个DTO编写单独的Service子类;形式B必须加
{}生成匿名子类,直接new GenericService<SomeData>()会报错
注意事项
如果泛型参数T本身是带泛型的复合类型(比如List<SomeData>),上述获取Class的方案不适用,需要将clazz的类型改为Type,Jackson的readValue方法原生支持传入Type参数进行反序列化。
内容的提问来源于stack exchange,提问作者Weeedooo
相关产品推荐
相关产品推荐

