You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

泛型抽象类继承场景下Jackson反序列化失败原因探究

问题: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 03:13:14