Jon Egeland
Jon Egeland

Reputation: 12613

Dynamic Return Type in Java method

I've seen a question similar to this multiple times here, but there is one big difference.

In the other questions, the return type is to be determined by the parameter. What I want/need to do is determine the return type by the parsed value of a byte[]. From what I've gathered, the following could work:

public Comparable getParam(String param, byte[] data) {
    if(param.equals("some boolean variable")
        return data[0] != 0;
    else(param.equals("some float variable") {
        //create a new float, f, from some 4 bytes in data
        return f;
    }
    return null;
}

I just want to make sure that this has a chance of working before I screw anything up. Thanks in advance.

Upvotes: 24

Views: 68425

Answers (7)

Rafat Khandaker
Rafat Khandaker

Reputation: 1

You can use Object class and "unbox" the value by checking type method.

  public Object getParam(String param, byte[] data) {
        if(param.equals("some boolean variable")
            return data[0] != 0;
        else(param.equals("some float variable") {
            //create a new float, f, from some 4 bytes in data
            return f;
        }
  }


    public boolean getBoolean( Object obj ) throws Exception{
      if( obj instanceof Boolean) return (boolean)obj ;
      throw new Exception("Not instance of Boolean");
    }

     public float getFloat( Object obj ) throws Exception{
      if( obj instanceof Float) return (float)obj ;
      throw new Exception("Not instance of Float");
     }

Upvotes: 0

SUNEEL KAUSHIK S
SUNEEL KAUSHIK S

Reputation: 135

It can also be done as the below example:

public class Test 
{
  public <T> T dynamicReturnMethod(Class<T> clazz) 
  {
    //your code
    return class.getDeclaredConstructor().newInstance();
  }

//usage
  public static void main(String[] args) 
  {
    Test t = new Test();
    ClassObjectWhichNeedsToBeReturned obj = 
       t.dynamicReturnMethod(ClassObjectWhichNeedsToBeReturned.class)
  }
}

This has been tested using java 11

Upvotes: 0

Thomas Decaux
Thomas Decaux

Reputation: 22681

My 2 cents with an example with Google HTTP client:

static public <Any> Any getJson(final String url, final Class<Any> parseAs) throws IOException {
        HttpRequestFactory requestFactory
                = HTTP_TRANSPORT.createRequestFactory(
                (HttpRequest request) -> {
                    request.setParser(new JsonObjectParser(JSON_FACTORY));
                });

        HttpRequest request = requestFactory.buildRequest(HttpMethods.GET, new GenericUrl(url), null);

        return request.execute().parseAs(parseAs);
    }

Can be use like this:

HashMap<String, Object> out = HttpUtils.getJson( "https://api.qwant.com", HashMap.class);

Upvotes: 3

Danieth
Danieth

Reputation: 415

This CAN be done. The following code will work:

public byte BOOLEAN = 1;
public byte FLOAT = 2;
public static <Any> Any getParam(byte[] data) {
    if (data[0] == BOOLEAN) {
        return (Any)((Boolean)(boolean)(data[1] != 0));
    } else if (data[0] == FLOAT) {
        return (Any)((Float)(float)data[1]);
    } else {
        return null;
    }
}

By using a generic for the return type any Java method can dynamically return any object or primitive types. You can name the generic whatever you want, and in this case I called it 'Any'. Using this code you avoid casting the return type when the method is called. You would use the method like so:

byte[] data = new byte[] { 1, 5 };
boolean b = getParam(data);
data = new byte[] { 2, 5 };
float f = getParam(data);

The best you can do without this trick is manually casting an Object:

float f = (float)getParam(data);

Java dynamic return types can reduce boilerplate code.

Upvotes: 28

ddyer
ddyer

Reputation: 1786

You can't do it. Java return types have to be either a fixed fundamental type or an object class. I'm pretty sure the best you can do is return a wrapper type which has methods to fetch various possible types of values, and an internal enum which says which one is valid.

--- edit --- after Danieth's correction!

public <Any> Any getParam(boolean b){
return((Any)((Boolean)(!b)));
}
public <Any> Any getParam(float a) {
 return((Any)((Float)(a+1)));
}
public <Any> Any getParam(Object b) {
 return((Any)b);
}
public void test(){
  boolean foo = getParam(true);
  float bar = getParam(1.0f);
  float mumble = getParam(this); // will get a class cast exception
}

You still incur some penalties for boxing items and type checking the returned values, and of course if your call isn't consistent with what the implementations of getParam actually do, you'll get a class cast exception.

Upvotes: 16

hisdrewness
hisdrewness

Reputation: 7651

I don't know what these people are talking about. You lose type safety, which is a concern, but you could easily accomplish this with generics...something like:

public <T> T getSomething(...) { }

or

interface Wrapper<T> { T getObject(); }

public <T> Wrapper<T> getSomething(...) { }

The latter promotes the possibility of a strategy pattern. Pass the bytes to the strategy, let it execute and retrieve the output. You would have a Byte strategy, Boolean strategy, etc.

abstract class Strategy<T> {
    final byte[] bytes;

    Strategy(byte[] bytes) { this.bytes = bytes; }

    protected abstract T execute();
}

then

class BooleanStrategy extends Strategy<Boolean> {
    public BooleanStrategy(byte[] bytes) { super(bytes); }

    @Override
    public Boolean execute() {
        return bytes[0] != 0;
    }

}

Your example code is a bad use case though and I wouldn't recommend it. Your method doesn't make much sense.

Upvotes: 31

Kane
Kane

Reputation: 4157

If you are really only returning a boolean or a float, then the best you can do is Object.

If you are returning variable objects, you have to choose a return type with the least common superclass. Primitives don't have a superclass, but they will be boxed into Object representations (like Boolean and Float) which have a common superclass of Object.

Upvotes: 1

Related Questions