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>
?
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
}
}