泛型方法中如何为ParameterizedTypeReference<T>指定T的实际类型?
问题描述
控制器定义了以下方法:
Mono<EntityModel<Some>> readSome(...) { // works, simply. } Flux<EntityModel<Other>> readOther(...) { // works, simply. }
测试类中原本的测试方法可以正常工作:
// WORKS! static List<EntityModel<Some>> readSome(...) { client... .exchange() .expectStatus().isOk() .expectBodyList(new TypeReferences.EntityModelType<Some>() { }) .returnResult() .getResponseBody(); } // WORKS! static List<EntityModel<Other>> readOther(...) { client... .exchange() .expectStatus().isOk() .expectBodyList(new TypeReferences.EntityModelType<Other>() { }) .returnResult() .getResponseBody() }
但尝试封装泛型方法后,Jackson无法将响应解析为实际类型,而是解析成了LinkedHashMap:
private static <T> List<EntityModel<T>> readList(final WebTestClient client, final Function<UriBuilder, URI> uriFunction, @Nullable final String accept) { final var responseBody = client .get() .uri(uriFunction) .headers(h -> { Optional.ofNullable(accept) .map(MediaType::valueOf) .map(List::of) .ifPresent(h::setAccept); }) .exchange() .expectStatus().isOk() .expectBodyList(new TypeReferences.EntityModelType<T>() { // 无法识别T的实际类型,解析为Map }) .returnResult() .getResponseBody(); return Objects.requireNonNull(responseBody, "responseBody is null"); }
需要解决:如何结合Class<T>与TypeReferences.EntityModelType<T>指定T的实际类型,让Jackson正确解析?
解决方案
核心问题是泛型类型擦除——编译后T的实际类型信息会丢失,导致Jackson无法推断目标类型,只能 fallback 到LinkedHashMap。以下是几种可行的解决方式:
方式1:传入Class<T>并构造带类型信息的TypeReference
修改泛型方法,添加Class<T>参数,通过该类对象构造保留实际类型的EntityModelType:
private static <T> List<EntityModel<T>> readList(final WebTestClient client, final Function<UriBuilder, URI> uriFunction, @Nullable final String accept, final Class<T> elementType) { final var responseBody = client .get() .uri(uriFunction) .headers(h -> { Optional.ofNullable(accept) .map(MediaType::valueOf) .map(List::of) .ifPresent(h::setAccept); }) .exchange() .expectStatus().isOk() // 基于传入的Class构造带实际类型的TypeReference .expectBodyList(new TypeReferences.EntityModelType<T>() { @Override public Type getType() { return TypeUtils.parameterize(EntityModel.class, elementType); } }) .returnResult() .getResponseBody(); return Objects.requireNonNull(responseBody, "responseBody is null"); }
调用时传入对应类型的Class:
List<EntityModel<Some>> someList = readList(client, uriBuilder -> ..., null, Some.class); List<EntityModel<Other>> otherList = readList(client, uriBuilder -> ..., null, Other.class);
方式2:直接传入完整类型的TypeReference
让调用方传入包含完整泛型信息的TypeReference,避免类型擦除问题:
private static <T> List<EntityModel<T>> readList(final WebTestClient client, final Function<UriBuilder, URI> uriFunction, @Nullable final String accept, final TypeReference<List<EntityModel<T>>> typeReference) { final var responseBody = client .get() .uri(uriFunction) .headers(h -> { Optional.ofNullable(accept) .map(MediaType::valueOf) .map(List::of) .ifPresent(h::setAccept); }) .exchange() .expectStatus().isOk() .expectBody(typeReference) .returnResult() .getResponseBody(); return Objects.requireNonNull(responseBody, "responseBody is null"); }
调用示例:
List<EntityModel<Some>> someList = readList(client, uriBuilder -> ..., null, new TypeReference<List<EntityModel<Some>>>() {});
方式3:利用Spring的ParameterizedTypeReference
Spring提供的ParameterizedTypeReference可以更简洁地处理泛型类型:
private static <T> List<EntityModel<T>> readList(final WebTestClient client, final Function<UriBuilder, URI> uriFunction, @Nullable final String accept, final ParameterizedTypeReference<List<EntityModel<T>>> typeRef) { final var responseBody = client .get() .uri(uriFunction) .headers(h -> { Optional.ofNullable(accept) .map(MediaType::valueOf) .map(List::of) .ifPresent(h::setAccept); }) .exchange() .expectStatus().isOk() .expectBody(typeRef) .returnResult() .getResponseBody(); return Objects.requireNonNull(responseBody, "responseBody is null"); }
调用示例:
List<EntityModel<Some>> someList = readList(client, uriBuilder -> ..., null, new ParameterizedTypeReference<List<EntityModel<Some>>>() {});
内容的提问来源于stack exchange,提问作者Jin Kwon
相关产品推荐
相关产品推荐

