Using Spring RestTemplate in generic method with generic parameter Using Spring RestTemplate in generic method with generic parameter java java

Using Spring RestTemplate in generic method with generic parameter


No, it is not a bug. It is a result of how the ParameterizedTypeReference hack works.

If you look at its implementation, it uses Class#getGenericSuperclass() which states

Returns the Type representing the direct superclass of the entity (class, interface, primitive type or void) represented by this Class.

If the superclass is a parameterized type, the Type object returned must accurately reflect the actual type parameters used in the source code.

So, if you use

new ParameterizedTypeReference<ResponseWrapper<MyClass>>() {}

it will accurately return a Type for ResponseWrapper<MyClass>.

If you use

new ParameterizedTypeReference<ResponseWrapper<T>>() {}

it will accurately return a Type for ResponseWrapper<T> because that is how it appears in the source code.

When Spring sees T, which is actually a TypeVariable object, it doesn't know the type to use, so it uses its default.

You cannot use ParameterizedTypeReference the way you are proposing, making it generic in the sense of accepting any type. Consider writing a Map with key Class mapped to a predefined ParameterizedTypeReference for that class.

You can subclass ParameterizedTypeReference and override its getType method to return an appropriately created ParameterizedType, as suggested by IonSpin.


As the code below shows it, it works.

public <T> ResponseWrapper<T> makeRequest(URI uri, final Class<T> clazz) {   ResponseEntity<ResponseWrapper<T>> response = template.exchange(        uri,        HttpMethod.POST,        null,        new ParameterizedTypeReference<ResponseWrapper<T>>() {            public Type getType() {                return new MyParameterizedTypeImpl((ParameterizedType) super.getType(), new Type[] {clazz});        }    });    return response;}public class MyParameterizedTypeImpl implements ParameterizedType {    private ParameterizedType delegate;    private Type[] actualTypeArguments;    MyParameterizedTypeImpl(ParameterizedType delegate, Type[] actualTypeArguments) {        this.delegate = delegate;        this.actualTypeArguments = actualTypeArguments;    }    @Override    public Type[] getActualTypeArguments() {        return actualTypeArguments;    }    @Override    public Type getRawType() {        return delegate.getRawType();    }    @Override    public Type getOwnerType() {        return delegate.getOwnerType();    }}


I am using org.springframework.core.ResolvableType for a ListResultEntity :

    ResolvableType resolvableType = ResolvableType.forClassWithGenerics(ListResultEntity.class, itemClass);    ParameterizedTypeReference<ListResultEntity<T>> typeRef = ParameterizedTypeReference.forType(resolvableType.getType());

So in your case:

public <T> ResponseWrapper<T> makeRequest(URI uri, Class<T> clazz) {   ResponseEntity<ResponseWrapper<T>> response = template.exchange(        uri,        HttpMethod.POST,        null,        ParameterizedTypeReference.forType(ResolvableType.forClassWithGenerics(ResponseWrapper.class, clazz)));    return response;}

This only makes use of spring and of course requires some knowledge about the returned types (but should even work for things like Wrapper>> as long as you provide the classes as varargs )