深入理解TypeReference作用

该文章已生成可运行项目,

在Java中,泛型信息在编译后会被擦除,这意味着在运行时无法直接获取泛型的具体类型信息,

这时候就需要Java中的TypeReference

一、java泛型擦除

如下代码

List<String> list = new ArrayList<>();
list.add("Hello");
String s = list.get(0);

编译器处理后实际生成的字节码等价于:

List list = new ArrayList();  // 原始类型
list.add("Hello");            // 添加Object
String s = (String) list.get(0); // 编译器插入的强制转换

那这里为什么编译器会自动添加类型转换呢

步骤1:编译时类型约束

List<String> list = new ArrayList<>();
  • 编译器记录 list 的类型参数为 String

  • 所有后续操作都基于此约束检查

步骤2:添加元素验证

list.add("Hello"); // ✅ 通过
list.add(123);     // ❌ 编译失败

步骤3:获取元素转换

String s = list.get(0);

↓ 编译器转换为 ↓

String s = (String) list.get(0); // 插入强制转换

TypeReference提供了一种在运行时保留泛型类型信息的方法:

// 创建带有具体泛型信息的TypeReference
TypeReference<Map<String, String>> typeRef = new TypeReference<Map<String, String>>() {};
​
// 获取完整的类型信息
Type type = typeRef.getType();
System.out.println(type); // java.util.Map<java.lang.String, java.lang.String>

实际应用

常见的是json的解析中调用方法里会传入TypeRegerence参数

如果不使用TypeReference

// 编译时:Map<String, String>
// 运行时:只保留原始类型 Map
Map<String, String> map = new HashMap<>();
​
// 错误示例:类型信息丢失
//mapper.readValue就是把json字符串转化为第二个参数
Map<String, String> result = mapper.readValue(json, map.getClass());

在运行时,map.getClass() 返回的是 HashMap.class 或 Map.class,但不知道

  • 键的类型是 String

  • 值的类型是 String

所以

  • 键可能被解析为 Object 而非 String

  • 值可能被解析为 Object 而非 String

解决办法就是通过TypeReference

ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"John\", \"age\":\"30\"}";
​
// 使用TypeReference保留Map类型信息
Map<String, String> result = mapper.readValue(json, 
    new TypeReference<Map<String, String>>() {});

匿名内部类在运行时保留了 Map<String, String> 的完整类型信息

  • Java 编译器对泛型执行类型擦除,但有一个重要例外

  • 当类/接口被继承或实现时,编译器会在字节码中保留具体的泛型参数信息

  • 当创建匿名子类时:

    new TypeReference<Map<String, String>>() {}

    Java 编译器会生成一个类似如下的类(伪代码):

    class Anonymous1 extends TypeReference<Map<String, String>> {}
  • 这个生成的类在字节码中明确记录了父类的泛型参数 Map<String, String>

让我们看看源码

public abstract class TypeReference<T> {
    protected final Type _type;

    public TypeReference() {//TypeReference的构造器
        Type superClass = getClass().getGenericSuperclass();
        // 获取父类(TypeReference)的泛型参数:Map<String, String>
        _type = ((ParameterizedType) superClass).getActualTypeArguments()[0];
    }

    public Type getType() { return _type; }
}
方法作用返回值示例
getClass().getGenericSuperclass()获取当前类的泛型父类类型TypeReference<Map<String, String>>
(ParameterizedType) 强转转换为可提取泛型参数的类型参数化类型对象
getActualTypeArguments()[0]获取父类的第一个泛型参数

Map<String, String>

在 Jackson 中的实际使用:

当调用 objectMapper.readValue() 时:

objectMapper.readValue(json, new TypeReference<Map<String, String>>() {});

Jackson 内部会调用:

TypeReference ref = ...; // 您的匿名实例
JavaType type = objectMapper.getTypeFactory().constructType(ref.getType());

本文章已经生成可运行项目
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值