Skip to content

JSON 反序列化中 TypeReference 的使用

最近在重构一个项目的后端的过程中,遇到了一个有关 JSON 反序列化的问题。

有这样的一段Java代码:

java
public List<String> getIPs() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.readValue(this.broadcastServerIPs, 
                                new TypeReference<List<String>>() {});
        } catch (Exception e) {
             return new ArrayList<>();
        }
    }

这里使用的是 TypeReference 作为反序列化的模版。代码能正常编译,没有任何问题。

事实上,在最开始的第一版代码,AI给我推荐的是 List.class 作为反序列化的模版,但是代码检查出现了类型未知的提示,所以重新又让AI进行修改,得到了 TypeReference 的结果,此时不再出现报错。

为什么?

询问AI得知:Java 泛型只在编译期存在,在 Java 中,泛型类型在运行时会被擦除。也就是说,运行时 List<String>List<Integer> 都是 ListList.class 无法携带 <String> 信息。这意味着当我们使用泛型时,运行时无法直接获取到泛型的具体类型信息。

因此,Jackson 在运行时“看不见”你想要的是 String,只能靠显式告诉它。

而TypeReference 的巧妙之处在于:通过匿名内部类获得泛型信息。虽然 Java 会擦除泛型,但这个匿名类的父类签名仍保留了 List<String> 的完整类型。Jackson 正是通过反射读取这个签名,准确还原出目标类型。


顺带一提,Spring Boot项目中的 @RequestBody 注解在接收请求体中的 List<User> 时,由于 Spring MVC 的存在,也会面临同样问题;

一种比较可行的解决方案是包装一层 DTO 对象:

java
// 使用 DTO 包装泛型集合
public class IPListDTO {
    private List<String> ips;
    // getter/setter ...
}

// 然后在 Spring Controller 中直接接收这个 DTO
@PostMapping("/updateIPs")
public void update(@RequestBody IPListDTO ipList) {
    // ...
}

这样避免了 顶层泛型 (直接暴露的泛型),绕开类型擦除问题。

Java 的泛型擦除是一把双刃剑:它保证了与旧版本的兼容性,却牺牲了运行时的类型信息。TypeReference 是 Jackson 社区在语言限制下的一种临时应对。只有理解底层机制,才能更好地写出优美的代码。