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

Java泛型基类中Jackson反序列化对象失败问题排查

问题描述

运行代码时抛出如下类型转换异常:

ClassCastException: class BaseResult cannot be cast to class MyResult (BaseResult and MyResult are in unnamed module of loader 'app')

异常触发点在BaseAction类的execute方法:

public abstract class BaseAction<IT extends BaseInput, RT extends BaseResult> {
    public RT execute(final IT inObj) throws Exception {
        //...
        TypeReference<RT> typeRef = new TypeReference<RT>() {};
        return JacksonFunctions.jacksonObjectMapper.readValue(jsonString, typeRef);  // 抛出ClassCastException
    }
}

相关类定义如下:

基础类

BaseInput.java

public abstract class BaseInput {
}

BaseResult.java

@Data
@NoArgsConstructor
public class BaseResult {
    @JsonProperty("ErrorCode")
    private int errorCode = 0;

    @JsonProperty("ErrorMessage")
    private String errorMessage = "";
}

BaseAction.java

public abstract class BaseAction<IT extends BaseInput, RT extends BaseResult> {

    public RT execute(final IT inObj) throws Exception {
        //...

        TypeReference<RT> typeRef = new TypeReference<RT>() {};
        return JacksonFunctions.jacksonObjectMapper.readValue(jsonString, typeRef);  // 抛出ClassCastException
    }
}

实现类

MyInput.java

@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class MyInput extends BaseInput {
    private String title;
}

MyResult.java

@Data
@EqualsAndHashCode(callSuper = true)
@NoArgsConstructor
public class MyResult extends BaseResult {
    @JsonProperty("Title")
    private String title;

    @JsonProperty("Description")
    private String description;
}

MyAction.java

public class MyAction extends BaseAction<MyInput, MyResult> {
}

运行代码

MyInput inObj = new MyInput();
inObj.setTitle("Galaxy");

MyAction act = new MyAction();
MyResult resObj = act.execute(inObj); // 此处抛出异常

已将MyResult作为泛型参数RT传入BaseAction的子类MyAction,但Jackson反序列化时仍将RT识别为BaseResult而非MyResult,直接在MyAction中使用TypeReference<MyResult>则可正常工作,需明确泛型基类中无法生效的原因。

原因分析

核心问题是Java泛型的类型擦除机制。

编译阶段,Java会将泛型参数RT擦除为它的上界类型BaseResult,因此运行时BaseAction中的TypeReference<RT>实际等价于TypeReference<BaseResult>。Jackson基于这个类型信息反序列化时,会创建BaseResult实例,代码试图将其强制转换为MyResult,自然抛出ClassCastException。

而在子类MyAction中直接使用TypeReference<MyResult>时,该类型是具体的、未被擦除的实际类型,Jackson能准确创建MyResult实例,因此不会报错。

解决方案

有两种常见的解决方式:

1. 让子类提供具体的TypeReference

在BaseAction中定义抽象方法,让子类返回对应的TypeReference:

public abstract class BaseAction<IT extends BaseInput, RT extends BaseResult> {
    // 抽象方法,子类实现返回具体的TypeReference
    protected abstract TypeReference<RT> getResultTypeReference();

    public RT execute(final IT inObj) throws Exception {
        //...
        return JacksonFunctions.jacksonObjectMapper.readValue(jsonString, getResultTypeReference());
    }
}

子类MyAction实现该方法:

public class MyAction extends BaseAction<MyInput, MyResult> {
    @Override
    protected TypeReference<MyResult> getResultTypeReference() {
        return new TypeReference<MyResult>() {};
    }
}

2. 通过反射获取泛型实参类型

在BaseAction的构造函数中,通过反射获取子类传入的泛型实参RT的实际类型,构造对应的TypeReference:

public abstract class BaseAction<IT extends BaseInput, RT extends BaseResult> {
    private final TypeReference<RT> resultTypeRef;

    @SuppressWarnings("unchecked")
    public BaseAction() {
        // 获取当前类的泛型父类信息
        Type genericSuperclass = getClass().getGenericSuperclass();
        if (genericSuperclass instanceof ParameterizedType parameterizedType) {
            // 获取第二个泛型参数(RT对应的类型)
            Type type = parameterizedType.getActualTypeArguments()[1];
            resultTypeRef = new TypeReference<RT>() {
                @Override
                public Type getType() {
                    return type;
                }
            };
        } else {
            throw new IllegalArgumentException("当前类未使用泛型参数");
        }
    }

    public RT execute(final IT inObj) throws Exception {
        //...
        return JacksonFunctions.jacksonObjectMapper.readValue(jsonString, resultTypeRef);
    }
}

这种方式无需子类额外实现方法,基类通过反射自动获取实际类型,但反射会带来一定性能开销,代码复杂度略高。

内容的提问来源于stack exchange,提问作者bobyuan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 10:17:08