ParameterizedTypeReference returning LinkedHashMap when called as function with generics

988 views Asked by At

Just curious about this behavior when using Type ParameterizedTypeReference

This is working

ArrayResponse<Submission> block = webClient
                .get()
                .uri(newSubmissionsUri)
                .attributes(clientRegistrationId(CLIENT_REGISTRATION_ID))
                .retrieve()
                .bodyToMono(new ParameterizedTypeReference<ArrayResponse<Submission>>() {
                })
                .block();

but this is not working:

ArrayResponse<Submission> block2 = webClient
                .get()
                .uri(newSubmissionsUri)
                .attributes(clientRegistrationId(CLIENT_REGISTRATION_ID))
                .retrieve()
                .bodyToMono(arrayResponse(Submission.class))
                .block();

private <T> ParameterizedTypeReference<ArrayResponse<T>> arrayResponse(Class<T> clz) {
    return new ParameterizedTypeReference<ArrayResponse<T>>() {
    };
}

There are no compilation errors. But at runtime, instead of Submission, I'm seeing LinkedHashMap when I debug.

Also tried:

ArrayResponse<Submission> block2 = webClient
                .get()
                .uri(newSubmissionsUri)
                .attributes(clientRegistrationId(CLIENT_REGISTRATION_ID))
                .retrieve()
                .bodyToMono(this.<Submission>arrayResponse())
                .block();

private <T> ParameterizedTypeReference<ArrayResponse<T>> arrayResponse() {
    return new ParameterizedTypeReference<ArrayResponse<T>>() {
    };
}

but again seeing LinkedHashMap

1

There are 1 answers

0
rnov On

As very often with generics in java, the answer is related to the type erasure.

Because of the erasure, you can't just get the generic type information from the class itself, but it's possible to get it from a parent class. That's why Spring and Jackson have to rely on Class.getGenericSuperclass() to get the information about your generic type. When you do

new ParameterizedTypeReference<ArrayResponse<Submission>>() {}

you're creating a new subclass that extends ParameterizedTypeReference<ArrayResponse<Submission>>, and Spring can use it to get the type parameter from its getGenericSuperclass. However, note an important thing: the type is known at compile time, and can't change. It's now really a part of the class.

On the other hand, when you try to do

private <T> ParameterizedTypeReference<ArrayResponse<T>> arrayResponse() {
    return new ParameterizedTypeReference<ArrayResponse<T>>() {};
}

the compiler doesn't really substitute T with the type, because the method might have been called in any place and with any type. This T now also becomes a part of the ParameterizedTypeReference subclass.

If you call this method (thus, providing the actual type to ParameterizedTypeReference) the result of getGenericSuperclass will still contain ArrayResponse<T>, with T being an instance of java.lang.reflect.TypeVariable.