Reputation: 3054
I am trying to write a generic method so I can reuse it for multiple Request and response types. This is the non-generic method
public ReservationBookingResponse invokeService(String endPoint, ReservationBookingRequest reservationBookingRequest, HttpServletRequest httpServletRequest) throws APIException {
ResponseEntity<ReservationBookingResponse> response = null;
try {
String authToken = getAuthToken(restTemplate, httpServletRequest);
HttpEntity<ReservationBookingRequest> entity = new HttpEntity<ReservationBookingRequest>(reservationBookingRequest, addAuthorizationHeader(authToken));
response = restTemplate.postForEntity(endPoint, entity, ReservationBookingResponse.class);
} catch (Exception e) {
throw new APIException(e);
}
return response.getBody();
}
This is the generic method I wrote
public default T invoke(String endPoint, K requestBean, HttpServletRequest httpServletRequest) throws APIException {
RestTemplate restTemplate=new RestTemplate();
ResponseEntity<T> response = null;
try {
String authToken = getAuthToken(restTemplate, httpServletRequest);
HttpEntity<K> entity = new HttpEntity<K>(requestBean, addAuthorizationHeader(authToken));
response = restTemplate.postForEntity(endPoint, entity, T);
} catch (Exception e) {
throw new APIException(e);
}
return response.getBody();
}
Now the problem is in response = restTemplate.postForEntity(endPoint, entity, T);
I am getting an exception "T cannot be resolved to a type" in the non generic(former) method response = restTemplate.postForEntity(endPoint, entity, ReservationBookingResponse.class);
this works fine.
Now I can not do T.class as it is not allowed. Can someone please tell me how to pass Class<T>
to the method.
This is what the postforEntity method expects
@Override
public <T> ResponseEntity<T> postForEntity(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException {
Upvotes: 2
Views: 8748
Reputation: 3054
This is what I had to do to make to make it work, pass the class type as an argument from calling method.
Quick fix Solution -1
public T postRequestToService(String endPoint, K requestBean, HttpServletRequest httpServletRequest,
Class<T> classType) throws APIException {
response = restTemplate.postForEntity(endPoint, entity, classType);
and in the calling method
postRequestToService(endPoint+addOnAvailability, addOnAvailabilityRequest,
httpServletRequest, AddOnAvailabilityResponse.class);
However a better solution is below one - Solution -2
Define a constructor, even though this happens to be an abstract class
public class PegasusService<K, T> {
private Class<K> requestClass;
private Class<T> responseClass;
public PegasusService(Class<K> requestClass, Class<T> responseClass) {
this.requestClass = requestClass;
this.responseClass = responseClass;
}
and in the child class pass the class types in the constructor.
AddOnAvailabilityService() {
super(AddOnAvailabilityRequest.class, AddOnAvailabilityResponse.class);
}
So this code works fine response = restTemplate.postForEntity(endPoint, entity, responseClass);
No need to pass the class type.
This way we dont have to add an extra argument or change the method definition.
Upvotes: 0
Reputation: 5546
You need to be given the Class<T>
at some point by the caller.
The most straightforward way is to require the caller to provide the class when calling invoke
. It is what Piotr Wilkin suggests and what you did in your answer. It however creates more boilerplate than needed, and there is a better option.
Since T
is a generic of the whole object, you could provide the Class<T>
to the constructor, like:
private final Class<T> type; // some prefer naming like clazz
public MyClassThatHasTheInvokeMethod(Class<T> type) { /* set the field(s) here */ }
Then, you can easily provide the type
to any method call that needs Class<T>
.
Upvotes: 2
Reputation: 3491
You can't.
Because of type erasure, the type T
is basically not provided at runtime in any way. The only case in which you can ever retrieve a type T
is in case of a non-generic class that inherits from a specific case of a generic class (i.e. Foo extends Bar<Something>
), in this case you can retrieve the Something
.
In all other cases, unfortunately, you have to use boilerplate - pass the class object explicitly. If any of your constructor arguments is an object of type T
, you can use .getClass()
instead to avoid the boilerplate, otherwise you have to pass the class manually.
Upvotes: 0