Jigar Prajapati
Jigar Prajapati

Reputation: 83

how to return different datatypes from single method?

I have below code.

I want to get different error messages based on status code, and if success that it should return object.. how to return this kind of different datatypes from one method?

public EstimateTimeResult getEstimateTime(@RequestBody EstimateTimeCall estimateTimeCall,@PathVariable("empid") Long empid) throws IOException{
        String URL=baseApi+"/estimates/time";
        if(estimateTimeCall.getProduct_id().isEmpty() || estimateTimeCall.getProduct_id()==null || estimateTimeCall.getProduct_id()==""){
            URL+="?start_latitude="+estimateTimeCall.getStart_latitude().toString()+"&start_longitude="+estimateTimeCall.getStart_longitude().toString();
        }else{
            URL+="?start_latitude="+estimateTimeCall.getStart_latitude().toString()+"&start_longitude="+estimateTimeCall.getStart_longitude().toString()+"&product_id="+estimateTimeCall.getProduct_id();
        }
        //Header Set
        HttpHeaders requestHeaders=getHeader(empid);
         //Do Request
         HttpEntity requestEntity=new HttpEntity(requestHeaders);
         ResponseEntity<EstimateTimeResult> response=restTemplate.exchange(URL,HttpMethod.GET,requestEntity,EstimateTimeResult.class);  
         org.springframework.http.HttpStatus statusCode=response.getStatusCode();            
         if(statusCode.value()==422){
             return "Required start latitude,longitude";
         }
         return  response.getBody();                    
    }

Upvotes: 0

Views: 1031

Answers (4)

GhostCat
GhostCat

Reputation: 140427

Simple: not possible in Java (unless you change the return type to Object, but that is super bad practice). But well, "not recommended" would be the better term.

Of course, you could do something like this

class RequestResult {
  static enum Result { PASS, FAIL };
  private Whatever dataForPass;
  private SomethingElse dataForFail;

  Whatever get(){
    return get((dataForFail)->throw new RequestError(dataForFail));
  }

  Whatever get(ErrorHandler handler){
    if(dataForFail != null){
      return handler.handle(dataForFail);
    }
    return dataForPass;
  }
}

interface ErrorHandler{
   Whatever handle(SomethingElse dataForFail) throws RequestError;
}

class RequestError extends RuntimeException{
    SomethingElse data;
    RequestError(SomethingElse data){
         this.data=data;
    }
}

then you can let the RequestResult handle how to get the Whatever/SomethingElse data, for example:

Whatever data = result.get((dataForFail) -> ...);
//or throws a RequestError if `SomethingElse` presented.
Whatever data = result.get(); 

and then create such an object; and depending on the actual outcome setup that object with the required data. By doing so, the return type of that method could be RequestResult; and you could use that for passing and failing requests. But that isn't the "standard practice".

Instead: you only return something when the method passes. Otherwise you throw an appropriate exception, containing the information the caller needs to put up a useful error message.

In other words: you should clearly separate the "happy" and the "unhappy" path. In Java, you do that by throwing exceptions when things go really wrong.

Upvotes: 6

CR Drost
CR Drost

Reputation: 9807

The claim is that this is not possible in Java: while literally it's not possible, there exist libraries to do this safely, inspired by the same safe interface coming from other programming languages.

One of the more promising in my quick browse-through was @palatable's lambda, which defines the idea as:

public abstract class Either<L, R> {
    private Either () {}
    // how you make these
    public static <L, R> Either<L, R> left(L l) {  return new Left<>(l); }
    public static <L, R> Either<L, R> right(R r) { return new Right<>(r); }
    // how to use these
    public abstract <V> V match(Function<? super L, ? extends V> leftFn, 
                                Function<? super R, ? extends V> rightFn);

    /* ~ some convenience methods to have a common interface to handle these things ~ */

    // the actual classes
    private static final class Left<L, R> extends Either<L, R> {
        private final L l;
        private Left(L l) {
            this.l = l;
        }

        @Override
        public <V> V match(Function<? super L, ? extends V> leftFn, 
                           Function<? super R, ? extends V> rightFn) {
            return leftFn.apply(l);
        }

        /* some other functions to get .equals() working right etc. */
    }
    // same for Right but with "R" swapped for "L"
}

Once you define this once, you can have the actual return type that you want as Either <String, ResponseBody> and you can return Either.left("hey, it's a string") or else return Either.right(response.getBody()) and then you can handle both types as data.

As many others have noticed, another way to handle an Either <String, x> type is as an exception that breaks the normal application logic flow; you can even define a more general exception. However remember that this will skip through any common logic that you might want to do for both of these. You need to decide whether there is any shared application logic between what you want to do with the response body and the error String, or whether you want to have this sort of goto statement that jumps right into a designated catch block instead. Those answers may be different for different applications.

Upvotes: 1

freedev
freedev

Reputation: 30067

I suppose this in your question should be a JAX-RS REST service.

So I want just show what's my way to do the things, I don't say is correct either perfect.

In a REST service I return an object like this

class APIResponse {
  private Object content = null;
  private Integer code = null;
  private String message = null;
  ... getter, setter and what else
}

The class APIResponse is the common object returned by the REST endpoints:

  public Response questionIdGet(String id, SecurityContext securityContext)
  {
    StatusCode sc;
    try {
      sc = new APIResponse().content(surveyBuilder.getQuestionById(id)).code(200);
      return Response.ok()
          .entity(sc)
          .build();
    } catch (Exception e) {
      return Response.status(500)
         .entity(new APIResponse().content(e).code(200).message(e.getMessage()))
          .build();
    }
  }

In this way your REST clients receive a common object where they know where find the content { "content" : object } and check if it is valid or not { "status" : 200, "message" : "error..." } .

Upvotes: 0

Optional
Optional

Reputation: 4507

Wrap it into an object that handles both or throw an exception for error status and handle it.

After all 422 is an error condition for your, so throw an exception

Upvotes: 0

Related Questions