泛型抽象类继承场景下Jackson反序列化失败原因探究
明明ConcreteServiceOne和ConcreteServiceTwo已经指定了泛型T对应的具体类型,为何第一个方法调用失败,第二个传入TypeReference参数的方法却能成功?为何new TypeReference<T>(){}无法利用子类指定的泛型类型,而传入的TypeReference参数即使通过类型推断创建也能生效?
对象定义
public abstract class BaseObject { public String someField; } public class ConcreteObjectOne extends BaseObject {} public class ConcreteObjectTwo extends BaseObject {}
反序列化类定义
public abstract class BaseService<T extends BaseObject> { public T deserializeObjectFail(String json) throws JsonProcessingException { var mapper = JsonMapper.builder().build(); return mapper.readValue(json, new TypeReference<T>() {}); } public T deserializeObjectSuccess(String json, TypeReference<T> typeReference) throws JsonProcessingException { var mapper = JsonMapper.builder().build(); return mapper.readValue(json, typeReference); } } public class ConcreteServiceOne extends BaseService<ConcreteObjectOne> {} public class ConcreteServiceTwo extends BaseService<ConcreteObjectTwo> {}
测试代码
var serviceOne = new ConcreteServiceOne(); var serviceTwo = new ConcreteServiceTwo(); // 抛出InvalidDefinitionException异常 serviceOne.deserializeObjectFail("{\"someField\":\"one\"}"); serviceTwo.deserializeObjectFail("{\"someField\":\"two\"}"); // 执行成功 serviceOne.deserializeObjectSuccess("{\"someField\":\"one\"}", new TypeReference<>() {}); serviceTwo.deserializeObjectSuccess("{\"someField\":\"two\"}", new TypeReference<>() {});
泛型类型擦除的核心限制:Java泛型是编译期特性,运行时会执行类型擦除。
BaseService中的泛型参数T在编译后会被擦除为它的上界BaseObject,所以在deserializeObjectFail方法里创建的new TypeReference<T>(){},运行时只能识别到BaseObject,无法获取子类ConcreteServiceOne/ConcreteServiceTwo指定的具体类型。Jackson没法从这个擦除后的类型推断出要反序列化的具体子类,因此抛出异常。传入的TypeReference能保留具体类型信息:当在测试代码中创建
new TypeReference<>(){}时,这个匿名内部类是在具体子类的调用上下文中生成的。编译器会根据当前serviceOne的实际泛型类型ConcreteObjectOne,把这个TypeReference的泛型信息填充为ConcreteObjectOne,并将该类型信息保存在匿名类的字节码中。Jackson可以通过反射读取这个保留的泛型信息,从而正确反序列化为目标类型。父类方法内创建TypeReference的局限性:在
BaseService的方法内部创建TypeReference<T>()时,这个匿名类的上下文是抽象父类,此时T已经被擦除,没有携带任何子类的具体泛型信息。Jackson只能看到BaseObject这个抽象类,无法确定具体的实现类,自然无法完成反序列化。
内容的提问来源于stack exchange,提问作者Mike Walton

