0

I have a candy factory that exposed various endpoints to get candies in a bag.

candy such as

  1. name: m&m ; endpoint: candy-factory/mnms
  2. name: skittle; endpoint: candy-factory/skittles
  3. name: smarties; endpoint: candy-factory/smarties

Api response pattern is below

 public class CandyWrapper <T> {
  @JsonProperty("candies")
  private List<T> candies;
}

I have a generic service class which deals with getting the candies using Spring RestTemplate

public class CandyStore<T> {

  private final RestTemplate restTemplate;

  private final Class<CandyWrapper<T>> type

  String candyFactroyUrl;

  
  public CandyStore(Class<CandyWrapper<T>> type, RestTemplate restTemplate, String 
   factoryUrl) {
    this.type = type;
    ....
    ....
   }

  public CandyWrapper<T> getCandies(){
      
       HttpEntity<HttpHeaders> request = new HttpEntity<>();
       ResponseEntity<CandyWrapper<T>> response;
       response = restTemplate.exchange(candyFactroyUrl, HttpMethod.GET, request, type);
       
       return response.getBody();
   }

}

Now when I am going to configure the CandyStore service with the actual type of the Candy ; Unable to determine the candy type to pass in the service constructor.

@Bean()
public getSkittleCandyStore(RestTemplate restTemplate, String skittleUrl){

  //TODO
   get the class of type CandyWrapper<Skittle> so that I can pass it to the service 
   constructor. 
   
   CandyWrapper<Skittle> instance = new CandyWrapper<Skittle>();

   // instance.getClass() does not give the class conforming to the parameter below.
  return new CandyStore(???, restTemplate, skittleUrl);

}

Any help to clear up the generics concepts is appreciated. Thanks!!

barun
  • 393
  • 1
  • 5
  • 19

1 Answers1

1

Java generics undergo type erasure, meaning they don't retain their generic type at runtime, so CandyWrapper<Skittle>.class just wont' work.

You may try to use ParameterizedTypeReference from Spring instead, which provides an abstract class that you simply subclass to capture the generic type, like:

@Bean
public CandyStore<Skittle> getSkittleCandyStore(
    RestTemplate restTemplate,
    String skittleUrl
) {
    return new CandyStore<>(
        new ParameterizedTypeReference<CandyWrapper<Skittle>>() {},
        restTemplate,
        skittleUrl
    );
}

and CandyStore:

public class CandyStore<T> {

    private final RestTemplate restTemplate;
    private final ParameterizedTypeReference<CandyWrapper<T>> typeRef;
    String candyFactroyUrl;

    public CandyStore(ParameterizedTypeReference<CandyWrapper<T>> typeRef, RestTemplate restTemplate, String factoryUrl) {
        this.typeRef = typeRef;
        this.restTemplate = restTemplate;
        this.candyFactroyUrl = factoryUrl;
    }

    public CandyWrapper<T> getCandies(){
        HttpEntity<HttpHeaders> request = new HttpEntity<>();
        ResponseEntity<CandyWrapper<T>> response;
        response = restTemplate.exchange(candyFactroyUrl, HttpMethod.GET, request, typeRef);
        return response.getBody();
    }
}

so please, make sure to update the restTemplate.exchange to use the new typeRef field instead, should looks like:

response = restTemplate.exchange(candyFactroyUrl, HttpMethod.GET, request, typeRef);

Read more about ParameterizedTypeReference:

Yahor Barkouski
  • 1,361
  • 1
  • 5
  • 21
  • 1
    Thank you Yahor for taking the time to respond. Your solution worked. I actually missed that, RestTemplate has an exchange method with ParameterizedTypeReference. – barun Jul 29 '23 at 15:19