views:

194

answers:

4

For example, I have a method that looks through a string for data separated by a specified deliminator, but some items might be a names, and other items might be numbers.

If a user calls my method to return item number X from the deliminated list, i want it to return a string if item X is a name, or a double if item X is a number.

For example, objectName.get(5); would get the 5th item in the deliminated list.

Would I have to use some type of overloading for this?

Or would I have to instead do something like objectName.getDouble(5); and objectName.getString(5); based on the fact that the user knows what item 5 is?

But what if the user doesn't know what item 5 is? He just needs a String or a Double depending on what it happens to be.

+4  A: 

Here's one way to do this:

public Object get() {
    if (blueMoon) {
        return new Double(42.0);
    } else {
        return "fred";
    }
}

Note that this will return a Double wrapper rather than a double.

I don't think this is a good idea though, since the caller now has to test the type of the returned value and do a typecast to do something with it.

For the record, Java does not allow a method to return a String or double because these types do not have a common supertype in the Java type system.

Stephen C
That's interesting! I don't get your second statement though... I return Strings and doubles all the time. Do you mean you can't return a String or a double if the return type is "Object"?
trusktr
If your return type is Object, you can return anything from this method that inherits from the Object base class. The problem is that the caller will not know what type you have returned, so the caller will have to do type checking and a cast to get anything useful from the return value. There's nothing wrong with that, but there's probably a cleaner way to do it.
Andy White
@trusktr - No. I mean that if the code that gets the value needs to do something with it *that depends on its type*, then it has to type cast it. Obviously, some things don't depend on the returned object's type; e.g. assigning to another `Object` variable, or calling `toString()`.
Stephen C
Man, miss PHP's extremely flexible type system in Java! hehe.
trusktr
@Stephen, could you please post an example of type casting the returned `Double` to use as an int by the code that used `get()`
trusktr
`int i = ((Double) get()).intValue();` But this is pretty dodgy. If the value is too big you get `Integer.MAX_VALUE` without any warning.
Stephen C
Oops, i meant to type cast the Double into a double. It was a typo, hehe. It will just unbox automatically, right? so `double d = x.get()`?
trusktr
@trusktr: I also "miss" PHP's runtime surprises in complex applications :)
cherouvim
@trusktr: no - unbox doesn't work here without a typecast. So `double d = (Double) x.get();`. And of course the typecast may throw an unchecked exception.
Stephen C
Thanks Stephen, I'm learning tons!
trusktr
A: 

The Java language does not expose an overload on the return type of a method. (As Thilo pointed out, this is a restriction of the Java language and not the JVM/bytecode.)

Generally this type of thing does not fit well into the Java type system. One could imagine returning an Either<String,Double> type (a more restricted return type than Object as suggested by Stephen C and a more general type than DoubleOrString as pointed out by B. Bear), but the general effort required to use such a construct in Java generally results in simply having multiple methods, e.g. getString(...) and getDouble(...).

pst
True that. I just want to be able to get whatever it is, without knowing the return type at first.
trusktr
How exactly does that Either type work?
trusktr
@trusktr - it is **hypothetical**.
Stephen C
Oh, gotcha. (foobar)
trusktr
"The Java language does not expose an overload on the return type of a method." The JVM however, does support this feature.
Thilo
@Thilo Is that so? Is there any way to make use of it?
trusktr
+3  A: 

For this sort of thing, I prefer to use something akin to the Maybe/Option pattern from the functional programming camp. You end up with an interface like:

public abstract class DoubleOrString 
{
    // Constraint isDouble() xor isString()
    public boolean isDouble();
    public boolean isString();

    //Must throw iff !isString()
    public String getString();

    //Must throw iff !ifDouble()
    public Double getDouble();

    public static DoubleOrString wrap(final double wrapMe)
    {
       return new DoubleOrString()
       {
             public boolean isDouble() {return true;}
             public boolean isString() {return false;}
             public Double getDouble() {return wrapMe;}
             public String getString() {throw new RuntimeException();}
       };
    }

    //same for wrap(String)
}

This forces the issue for clients, in that there is always a sanity check that there was indeed a double or String at the appropriate time. In your case, I'd make just one get() method, so when the client (thinks they) knows what the type is, the call is

objectName.get(5).getString();

and in your get(int) method, rather than returning a String or a double, the return statement looks like

DoubleOrString.wrap(theThingToReturn)

It's a little extra work up front, but it has paid of for me several times in the past.

Burleigh Bear
Wow, that seems interesting. I've never seen such a construct before. Can you edit the answer to explain a little bit more how it works?
trusktr
I'd reckon you'd get a lot more out of it if you tried it to get it to compile, and then played with it in a debugger. Which bit do you want explained? There's not much more context in your question to expound on.
Burleigh Bear
That's a clever trick. I'll have to remember that one.
mellowsoon
I guess I just don't know enough Java yet to even know what to ask hehe. I've been doing JAVA only for 4 weeks. I'll definitely keep this in handy though. For now, I just outputted Strings and let the client convert to double if he wishes.
trusktr
+2  A: 

If you need to do this then there is problem with your design. Since the original datasource is String you have to accept that all returned values will be string and leave it to the client to check whether the result can be converted to a number.

If you want to save the client from doing the check, you can provide him with a minimal API which may look something like:

public class ValueExtractor {

    public ValueExtractor(String delimitedText) {
        // ...
    }

    /**
     * Determines whether there is a next element
     * to be returned
     */
    public boolean next() {
        // ...
    }

    public String get() {
        // ...
    }

    /**
     * Returns the value as a Double if possible
     * null otherwise.
     */
    public Double getPossibleDouble() {
        // ...
    }
}
cherouvim
+1: "If you need to do this then there is problem with your design."
andersoj