Dynamic Return Type in Java method

53,349

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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);

Solution 5

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.

Share:
53,349
Jon Egeland
Author by

Jon Egeland

I want to make people's work more enjoyable. I like projects that allow people to be more effective at what they do, to work more quickly, and to feel accomplished. I've been a developer in some regard for more than half of my life and want to continue applying what I've learned to new experiences. I don't like hype. I'm not ready to "take it to the next level". I don't care how much you raised in Series A. I want a company that's built on itself and its merit, not other people. I've never been good at keeping these up to date, so just follow me on GitHub for an idea of what I'm doing at any given time.

Updated on November 21, 2020

Comments

  • Jon Egeland
    Jon Egeland over 3 years

    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.

  • Daniel Pryden
    Daniel Pryden over 12 years
    I almost upvoted, but you say that Object is the common superclass of primitives. That's not actually true in Java. Primitives have no common superclass, although they can be boxed into reference type equivalents that do.
  • Jon Egeland
    Jon Egeland over 12 years
    might you be able to provide an example? Or point me towards one? I understand what you're saying to do, but it would be a lot easier to look at the code for it.
  • Kane
    Kane over 12 years
    You are correct in that primitives are not Objects, but will be boxed into those automatically. I'm updating my answer to reflect this.
  • Daniel Pryden
    Daniel Pryden over 12 years
    I think you're on the right track here, but I don't quite follow what you're proposing. In the question, Jon has a byte[] and wants to pass in a String and get back a value that he can assign to a primitive without casting. I don't think that's actually possible. You're right, if instead of passing in a String, you pass some more complex object, you can utilize generics to get the right kind of value back.
  • Daniel Pryden
    Daniel Pryden over 12 years
    On the other hand, there's no way you can implement your T getSomething() method without at least a unchecked cast, so you potentially fail at runtime if it's used incorrectly (e.g. Boolean foo = getSomething("someFloatParam");). At that point, you're probably better off just using an explicit cast, since then it's clear that you could get a ClassCastException at that point.
  • ddyer
    ddyer over 12 years
    public class genericvalue { String returnStringvalue(){} int returnIntvalue() {} } and so on, where each method does whatever it takes to produce a value of the desired type.
  • Danieth
    Danieth almost 9 years
    @ddyer your answer is incorrect. You can have dynamic return types in Java. Look at my answer below for an example tailored to this question.
  • ddyer
    ddyer almost 9 years
    @danieth Wow! I had completely missed this bit of generic type magic. It seems that for fundamental types it costs boxing and unboxing the item, and it always costs a type check on the receiving end, but it definitely can be used to make cleaner and more natural code.