泛型创建的TypeReference失效:两种Jackson JSON解析方式差异探究
我尝试将JSON字符串解析为Map<String, Map<Long, Long>>类型。最初使用如下泛型方法,通过Jackson的ObjectMapper创建TypeReference<T>进行反序列化:
public static <T> T to(String jsonString) { TypeReference<T> typeReference = new TypeReference<T>() {}; return WrapThrowingSupplier.wrapIOException(() -> new ObjectMapper().readValue(jsonString, typeReference)); }
调用方式为:
Map<String, Map<Long, Long>> map = to(jsonString);
但此方法无法正常工作,遍历内层Map键集时抛出ClassCastException: class java.lang.String cannot be cast to class java.lang.Long异常。而使用如下直接创建匿名内部类的方式却能正常解析:
Map<String, Map<Long, Long>> map2 = new ObjectMapper().readValue(jsonString, new TypeReference<>() {});
请问这两种TypeReference创建方式的差异是什么?
核心差异:泛型类型信息的保留与否
这两种写法的本质区别在于泛型类型擦除时是否保留了具体的类型参数信息:
泛型方法中的
new TypeReference<T>() {}
在泛型方法内部创建TypeReference<T>时,Java的泛型擦除机制会把T擦除为它的上限类型(此处为Object)。Jackson无法通过这个TypeReference获取到Map<String, Map<Long, Long>>的具体泛型参数,只能默认将JSON中的键解析为String类型,后续强转Long时自然抛出ClassCastException。直接创建的
new TypeReference<>() {}
这种写法是在具体类型上下文中创建匿名内部类,编译器会把当前上下文的完整泛型类型信息(即Map<String, Map<Long, Long>>)嵌入到匿名内部类的字节码中。Jackson可以通过反射读取到这些具体类型信息,从而正确将内层Map的键反序列化为Long类型。
简言之,第一种写法的TypeReference丢失了目标类型的泛型参数细节,第二种写法的TypeReference完整保留了目标类型的结构信息。
内容的提问来源于stack exchange,提问作者Hi Jonk

