Generic method, equality constraint

Clash Royale CLAN TAG#URR8PPPGeneric method, equality constraint
Hello Im trying to implement a generic method as controller base method, but the problem which I cannot understand happens with the generic method signature.
<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, boolean isAdmin){
String sender = isAdmin ? adminHash : userHash;
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", sender);
headers.add("Content-Type", "application/json");
HttpEntity<T> entity = new HttpEntity<>(body,headers);
ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, body.getClass());
return responseEntity;
}
The compile error I currently have is as follows:
Incompatible equality constraint: T and capture of ? extends Object
2 Answers
2
You don't say exactly where the problem occurs, but I think this will occur on the restTemplate.exchange( call, as a result of passing body.getClass() as a parameter. This is because the return type of body.getClass() is Class<? extends Object>, as in the Javadoc:
restTemplate.exchange(
body.getClass()
body.getClass()
Class<? extends Object>
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called
Class<? extends |X|>
|X|
getClass
The problem is that you can't guarantee that body is specifically a T - it could be a subclass of T. As such, the result of body.getClass() might not be a Class<T>.
body
T
T
body.getClass()
Class<T>
If you want to be type-safe, you would need to pass that in as an additional parameter to the method.
<T> ResponseEntity<T> makeApiCall(
String path, HttpMethod httpMethod, T body, Class<T> bodyClass,
boolean isAdmin){
// ...
ResponseEntity<T> responseEntity =
restTemplate.exchange(
path, HttpMethod.POST, entity, bodyClass);
// ...
}
Note that the only way to obtain a Class<T> is to use a class literal, e.g. String.class if T is String. This precludes the use of generic body types, since there are no generic class literals.
Class<T>
String.class
T
String
getClass()
Class<T>
@LewBloch: I think what he means is that
T must be a reifiable type. Otherwise say if T is a parameterized type like Foo<Bar>, then you would somehow have to provide a Class<Foo<Bar>>, which doesn't really exist safely.– newacct
Feb 24 '17 at 8:11
T
T
Foo<Bar>
Class<Foo<Bar>>
You need to cast body.getClass() to Class<T>
cast body.getClass()
Class<T>
@SuppressWarnings("unchecked")
<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, boolean isAdmin){
String sender = isAdmin ? adminHash : userHash;
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", sender);
headers.add("Content-Type", "application/json");
HttpEntity<T> entity = new HttpEntity<>(body,headers);
ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, (Class<T>) body.getClass());
return responseEntity;
}
To make it type-safe you need to pass the class object as a parameter explicitly:
<T> ResponseEntity<T> makeApiCall(String path, HttpMethod httpMethod, T body, Class<T> clazz, boolean isAdmin){
String sender = isAdmin ? adminHash : userHash;
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", sender);
headers.add("Content-Type", "application/json");
HttpEntity<T> entity = new HttpEntity<>(body,headers);
ResponseEntity<T> responseEntity = restTemplate.exchange(path, HttpMethod.POST, entity, clazz);
return responseEntity;
}
Generic casts raise warnings that you should handle.
– Lew Bloch
Feb 18 '17 at 18:21
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
"the only way to obtain a Class<T> is to use a class literal, e.g. String.class if T is String. This precludes the use of generic body types, since there are no generic class literals" seems to contradict itself. Anyway, at compile time the compiler can figure out the generic type. And what about assigning a
getClass()result to aClass<T>variable?– Lew Bloch
Feb 18 '17 at 18:20