Search code examples
javaspringgenericsresttemplate

Spring RestTemplate: how to define the parameter type in a generic class


I'm writing a generic REST client to be used with different types of DTOs.

public abstract class GenericClient<DTO> {
  private final Class<DTO> dtoClass;
  ... ctor which sets dtoClass ...

  public DTO getOne(String id) {
    ...
    URI uri = ...
    ResponseEntity<DTO> exchange = getRestTemplate()
      .exchange(uri, HttpMethod.GET, entity, dtoClass);
    return exchange.getBody(); // works
  }

  public List<DTO> findAll() {
    ...
    URI uri = ...
    ResponseEntity<List<DTO>> exchange = getRestTemplate()
      .exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<DTO>() {});
    return exchange.getBody(); // fails with ClassCastException
  }
}

This is implemented by several concrete clients like this

public class OneActualDTOClient extends GenericClient<OneActualDTO> {
  public OneActualDTOClient(){
    super(OneActualDTO.class);
  }
}

This compiles, and the getOne method works fine. But for findAll, at runtime, the type parameter DTO is not available, so the exchange method cannot deserialize the incoming JSON (as far as I understand).

As you see, I pass the concrete DTO class (as a Class<DTO>) to use with exchange for single elementes (getOne).

How should I pass the ParameterizedTypeReference to the exchange method, so that at runtime it knows to return as List<OneActualDTO>?


Solution

  • So I found a solution, which is to override the getType method of ParameterizedTypeReference returning a custom made ParameterizedType like this:

    public abstract class GenericClient<DTO> {
      private final Class<DTO> dtoClass;
      ... ctor which sets dtoClass ...
    
    
      public List<DTO> findAll() {
        ...
        URI uri = ...
        ResponseEntity<List<DTO>> exchange = getRestTemplate()
          .exchange(uri, HttpMethod.GET, entity, new ParameterizedTypeReference<List<DTO>() {
            @Override
            public Type getType() {
                return new ParameterizedType() {
                    @Override
                    public Type getRawType() {
                        return List.class;
                    }
    
                    @Override
                    public Type getOwnerType() {
                        return null;
                    }
    
                    @Override
                    public Type[] getActualTypeArguments() {
                        return new Type[]{dtoClass};
                    }
                };
            });
        return exchange.getBody(); // OK
      }
    }