views:

2364

answers:

9

Since arguments sent to a method in Java point to the original data structures in the caller method, did its designers intend for them to used for returning multiple values, as is the norm in other languages like C ?

Or is this a hazardous misuse of Java's general property that variables are pointers ?

+1  A: 

You cannot truly return multiple values, but you can pass objects into a method and have the method mutate those values. That is perfectly legal. Note that you cannot pass an object in and have the object itself become a different object. That is:

private void myFunc(Object a) {
    a = new Object();
}

will result in temporarily and locally changing the value of a, but this will not change the value of the caller, for example, from:

Object test = new Object();
myFunc(test);

After myFunc returns, you will have the old Object and not the new one.

Legal (and often discouraged) is something like this:

private void changeDate(final Date date) {
    date.setTime(1234567890L);
}

I picked Date for a reason. This is a class that people widely agree should never have been mutable. The the method above will change the internal value of any Date object that you pass to it. This kind of code is legal when it is very clear that the method will mutate or configure or modify what is being passed in.

NOTE: Generally, it's said that a method should do one these things:

  • Return void and mutate its incoming objects (like Collections.sort()), or
  • Return some computation and don't mutate incoming objects at all (like Collections.min()), or
  • Return a "view" of the incoming object but do not modify the incoming object (like Collections.checkedList() or Collections.singleton())
  • Mutate one incoming object and return it (Collections doesn't have an example, but StringBuilder.append() is a good example).

Methods that mutate incoming objects and return a separate return value are often doing too many things.

Eddie
That's precisely my question. As with Date, should we not mutate any object passed by reference ?
euphoria83
Collections.sort() is a valid use, if one is required to not affect the original, pass the copy. Otherwise, its quite clear that the method sort would play with the passed list.
Adeel Ansari
Objects are never passed by reference; object *references* are passed by value. But that's not what this thread is about.
Alan Moore
+5  A: 

See this RFE launched back in 1999:

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4222792

I don't think the intention was to ever allow it in the Java language, if you need to return multiple values you need to encapsulate them in an object.

Using languages like Scala however you can return tuples, see:

http://www.artima.com/scalazine/articles/steps.html

You can also use Generics in Java to return a pair of objects, but that's about it AFAIK.

EDIT: Tuples

Just to add some more on this. I've previously implemented a Pair in projects because of the lack within the JDK. Link to my implementation is here:

http://pbin.oogly.co.uk/listings/viewlistingdetail/5003504425055b47d857490ff73ab9

Note, there isn't a hashcode or equals on this, which should probably be added.

I also came across this whilst doing some research into this questions which provides tuple functionality:

http://javatuple.com/

It allows you to create Pair including other types of tuples.

Jon
Lisp allows it beautifully too. Your solutions are correct and obvious, except for the use of Generics. Can you explain that in short as this use is new to me ?
euphoria83
I added the Pair implementation link and a link to javatuple - which looks pretty good. Hope that helps.
Jon
Hey thanx. This is great.
euphoria83
A: 

There are certainly methods that modify an object passed in as a parameter (see java.io.Reader.read(byte[] buffer) as an example, but I have not seen parameters used as an alternative for a return value, especially with multiple parameters. It may technically work, but it is nonstandard.

Nathan Voxland
It will definitely work. :)
euphoria83
+10  A: 

A long time ago I had a conversation with Ken Arnold (one time member of the Java team), this would have been at the first Java One conference probably, so 1996. He said that they were thinking of adding multiple return values so you could write something like:

x, y = foo();

The recommended way of doing it back then, and now, is to make a class that has multiple data members and return that instead.

Based on that, and other comments made by people who worked on Java, I would say the intent is/was that you return an instance of a class rather than modify the arguments that were passed in.

This is common practise (as is the desire by C programmers to modify the arguments... eventually they see the Java way of doing it usually. Just think of it as returning a struct. :-)

(Edit based on the following comment)

I am reading a file and generating two arrays, of type String and int from it, picking one element for both from each line. I want to return both of them to any function which calls it which a file to split this way.

I think, if I am understanding you correctly, tht I would probably do soemthing like this:

// could go with the Pair idea from another post, but I personally don't like that way
class Line
{
    // would use appropriate names
    private final int intVal;
    private final String stringVal;

    public Line(final int iVal, final String sVal)
    {
        intVal    = iVal;
        stringVal = sVal;
    }

    public int getIntVal()
    {
        return (intVal);
    }

    public String getStringVal()
    {
        return (stringVal);
    }

    // equals/hashCode/etc... as appropriate
}

and then have your method like this:

public void foo(final File file, final List<Line> lines)
{
    // add to the List.
}

and then call it like this:

{
    final List<Line> lines;

    lines = new ArrayList<Line>();
    foo(file, lines);
}
TofuBeer
I know exactly what you are saying, but constructing objects just for the purpose of returning multiple values has proved to be confusing in practice, certainly for me. It definitely goes against what a class is supposed to be.
euphoria83
It is not against what a class should be though. There are such things as "value classes" where the primary purpose is to simply holds state and not provide any behaviours. What is the confusion you have experienced?
TofuBeer
Just that then there exist a couple of class which are not doing anything except sitting there for value transfer. I try to make a class only if it does/holds something significant.
euphoria83
There are times where you may want to rethink the whole way you are doing things to get rid of those, there are other times that those make sense. Can you edit your question to provide a bit more context of what you are trying to accomplish?
TofuBeer
I am reading a file and generating two arrays, of type String and int from it, picking one element for both from each line. I want to return both of them to any function which calls it which a file to split this way. But I have faced this question in different situations many times before.
euphoria83
I am not 100% sure it will work for you, but I added to my post of what I might do in your shoes.
TofuBeer
Great. Thanks. I already know such tricks in the book. But appreciate your effort.
euphoria83
A: 

It's not generally considered terribly good practice, but there are very occasional cases in the JDK where this is done. Look at the 'biasRet' parameter of View.getNextVisualPositionFrom() and related methods, for example: it's actually a one-dimensional array that gets filled with an "extra return value".

So why do this? Well, just to save you having to create an extra class definition for the "occasional extra return value". It's messy, inelegant, bad design, non-object-oriented, blah blah. And we've all done it from time to time...

Neil Coffey
It does make sense for arrays. Atleast it does to me having done this in C and C++.
euphoria83
+3  A: 

I do wish there was a Pair<E,F> class in JDK, mostly for this reason. There is Map<K,V>.Entry, but creating an instance was always a big pain.

Now I use com.google.common.collect.Maps.immutableEntry when I need a Pair

Hemal Pandya
actually Google is doing some great work in filling up the holes in Sun's Java implementation. These need not be mistakes. But they definitely were needed for better hacking.
euphoria83
+5  A: 

In my opinion, if we're talking about a public method, you should create a separate class representing a return value. When you have a separate class:

  • it serves as an abstraction (i.e. a Point class instead of array of two longs)
  • each field has a name
  • can be made immutable
  • makes evolution of API much easier (i.e. what about returning 3 instead of 2 values, changing type of some field etc.)

I would always opt for returning a new instance, instead of actually modifying a value passed in. It seems much clearer to me and favors immutability.

On the other hand, if it is an internal method, I guess any of the following might be used:

  • an array (new Object[] { "str", longValue })
  • a list (Arrays.asList(...) returns immutable list)
  • pair/tuple class, such as this
  • static inner class, with public fields

Still, I would prefer the last option, equipped with a suitable constructor. That is especially true if you find yourself returning the same tuple from more than one place.

javashlook
A: 

Generally what Eddie said, but I'd add one more:

  • Mutate one of the incoming objects, and return a status code. This should generally only be used for arguments that are explicitly buffers, like Reader.read(char[] cbuf).
Kevin Peterson
A: 

I had a Result object that cascades through a series of validating void methods as a method parameter. Each of these validating void methods would mutate the result parameter object to add the result of the validation.

But this is impossible to test because now I cannot stub the void method to return a stub value for the validation in the Result object.

So, from a testing perspective it appears that one should favor returning a object instead of mutating a method parameter.

sudr