2

I have few methods which send request and should return response of specific type. all of the requests extends RequestVO type, and all of the responses extends ResponseVO.

To avoid casting in each method that return response , I have used generic method(see send method below).

After each request sent, even if it has failed, I need to save the response in Database.

The problem is in responseVO = new ErrorResponseVO(e);, it produces compiler error : Type mismatch: cannot convert from ErrorResponseVO to T.

How can I avoid this without casting?

@Override
public AuthSignResponseVO authenticate(AuthRequestVO authRequestVO) throws RegulationException{
    return send(authRequestVO, AuthSignResponseVO.class);

}

@Override
public AuthSignResponseVO sign(SignRequestVO signRequestVO) throws RegulationException{
    return send(signRequestVO, AuthSignResponseVO.class);

}

@Override
public CollectResponseVO collect(CollectRequestVO collectRequestVO) throws RegulationException{
    return send(collectRequestVO, CollectResponseVO.class);

}

@Override
public CancelResponseVO cancel(CancelRequestVO cancelRequestVO) throws RegulationException{
    return send(cancelRequestVO, CancelResponseVO.class);

}

private <T extends ResponseVO> T send(RequestVO requestVO, Class<T> responseType) throws RegulationException{
    HttpHeaders headers = new HttpHeaders();
    HttpEntity<RequestVO> httpEntity = new HttpEntity<>(requestVO,headers);
    ResponseEntity<T> responseEntity = null;
    T responseVO = null;
    try{
        responseEntity = restTemplate.postForEntity(url, httpEntity, responseType);
        responseVO = responseEntity.getBody();
    }catch(RestClientException e){
        responseVO = new ErrorResponseVO(e);
        throw new RegulationException(RegulationResponseCode.GeneralError);
    }finally{
        //save in db the response
    }
    return responseVO;
}
Ori Marko
  • 56,308
  • 23
  • 131
  • 233
Erez Levi
  • 81
  • 4
  • 1
    you re-throw the exception, why do you need a return value in the `catch`? – Andrew Tobilko Dec 20 '18 at 09:50
  • Also, can you guarantee that the `ErrorResponseVO` is a subclass of any `T` that can be sent? Otherwise, I see no way that this code should work. – tucuxi Dec 20 '18 at 10:49

4 Answers4

0

"How can I avoid this without casting?" You can't.

User code can specify any T when call your method. So you have to cast.

talex
  • 17,973
  • 3
  • 29
  • 66
  • Spring `RestTemplate` `postForEntity` method is generic method, which expect to get responseType. how they expect handling errors ? – Erez Levi Dec 20 '18 at 09:53
0

Your problem is actually instantiating the generic type, and explanation can be found here: Instantiating generic objects in Java

So instead try something like:

responseVO = responseType.getConstructor(RestClientException.class).newInstance(e);
maslan
  • 2,078
  • 16
  • 34
  • @ErezLevi the explanation is in the link provided. First of all the generics are not really accessible at a runtime. To check it out you can create generic list, then use it as raw (without generic) - you can put any objects there and Java will not complain. It will only complain when you try to retrieve the objects using the reference with generic part, it will tell you classcastexception – maslan Dec 20 '18 at 10:15
0

To understand the reason for the compilation error, let me translate the semantics of the generic method in words. Method send says, give me a type, and you should have this as a response when I get called. In that description, ErrorResponseVO is not the Class Type the caller specified in the signature. The Java compiler follows the same rationale, and thus it cannot allow the assignment operation to happen because it violates the inferred type T.

Another reason why getting a compilation error is the right behavior is the specific methods you defined above. In a method like cancel, the response type should be CancelResponseVO which will not work if the generic method send responds with ErrorResponseVO. So, even if you cast it, you will get a runtime error.

Looking at the semantics of your code, it looks to me that ErrorResponseVO is more of an exceptional response that should be returned as an exception. If you are using Spring, you can intercept that exception and return a proper HTTP response with proper code to the client.

Hope that helps.

Eslam Nawara
  • 645
  • 3
  • 8
0

That wont work. Imagine that type T provided is a mouse and you are trying to make new cat from a mouse. You cannot define what type has been provided at compilation time. If you want to avoid casting, consider adding ErrorResponseVO object in your method and saving it in finally clause when responseVO is null. There are many approaches, that is just proposition.

Mershel
  • 542
  • 1
  • 9
  • 17
  • but i declare `T extends ResponseVO` and ErrorResponseVO extends ResponseVO. – Erez Levi Dec 20 '18 at 11:48
  • And there also can be `AnotherErrorVO` which also extends `ResponseVO`. What if `T` would be `AnotherErrorVO`. `` does not mean polymorphism behaviour like you expect. Read more [here](https://stackoverflow.com/questions/2745265/is-listdog-a-subclass-of-listanimal-why-are-java-generics-not-implicitly-po) – Mershel Dec 20 '18 at 11:54