Java Forum / General / April 2006
C++ programmer stumbles over pass by value. Need advice.
Lenny Wintfeld - 19 Apr 2006 01:29 GMT Here's a model of my problem. I see what's causing it (thinking in C++, with pass by reference) but I don't see a Java way to solve it. Probably because I'm so new to Java.
Here's the problem.
2 methods inside the same class. Method1 passes a String array to method2, relying on method2 to process the string array and return a String and a new String array for further processing back to method1.
public boolean method1 (String s1, other args) { //do some stuff String [] sa1 = null; // to be created and returned by method2 String s2 = null; // to be created and returned by method2
Method2(s1, sa1, s2)l
//do some important stuff with sa1 and s2
return true; }
// C++ type method (unfortunately) private void method2 (String s1, String [] sa1, String s2) { //do some stuff StringTokenizer st = new StringTokenizer(s1, "/");
String sRemainder;
sRemainder = st.nextToken(); s2 = st.nextToken(); // just a string to be returned
sa1 = sRemainder.split(","); // a string array to be returned }
The eclipse debugger confirms that method 2 is processing the strings exactly as I want it to. sa1 and s2 after the processing look great inside method 2. But they're null after the return to method1. I assume this is because sa1 and s2 are copies inside method 2 and they go out of scope on the return. sa2 and s1 are created inside method2 but I need them for further work in method 1.
I obviously coded this like a C/C++ programmer. What I'd like to know is what's the conventional methodology (or programming idiom) for passing back lots of stuff created in one method to it's caller? Feel free to hack up the above example to show me how, if it's convenient.
Thanks in advance for your help/
Lenny Wintfeld
Andrew McDonagh - 19 Apr 2006 01:55 GMT > Here's a model of my problem. I see what's causing it (thinking in C++, > with pass by reference) but I don't see a Java way to solve it. Probably [quoted text clipped - 21 lines] > > Lenny Wintfeld For starters, try to stop thinking in C++ - easier said than done admittedly, but worth it.
In Java, References are passed by value. The objects they point to aren't actually moved or copied at all.
Primitives (ints, boolean, long, etc) are always passed by value.
So this leaves only two ways of returning things from methods.
1) the method's returned type. In your example, you could change the method signature to return something rather than its current 'void'.
2) Pass in a reference to a Data holder object, inside the method set the value within the Dataholder to what ever it is you want to return.
Either way works for sending by single or multiple values. For multiple values it just means you'll have to wrap then in something.
Data holder passed in parameter list example:
class StuffReturnedByMethod2 ( String [] sa1 = null; // to be created and returned by method2 String s2 = null; // to be created and returned by method2 }
public boolean method1 (String s1, other args) { //do some stuff StuffReturnedByMethod2 returnedStuff = new StuffReturnedByMethod2(); Method2(s1, returnedStuff )
//do some important stuff with sa1 and s2
String[] sa1 = returnedStuff.sa1; String s2 = returnedStuff.s2;
return true; }
// C++ type method (unfortunately) private void method2 (String s1, StuffReturnedByMethod2 stuffToReturn) { //do some stuff StringTokenizer st = new StringTokenizer(s1, "/");
String sRemainder = st.nextToken(); stuffToReturn.s2 = st.nextToken(); // just a string to be returned
stuffToReturn.sa1 = sRemainder.split(","); // a string array to be returned }
This might look - hacky or cheap, but what we tend to find, is that we have more smaller classes in Java, which are highly focused upon one job. These classes might startout being mere data holders, but usually, very quickly they start attracting behaviour or state too.
Stefan Ram - 19 Apr 2006 02:16 GMT >In Java, References are passed by value. The objects they point to >aren't actually moved or copied at all. This is like in C++, for the case that
- one thinks of Java references as corresponding to C++ pointers (quotation from the JLS3, 4.3.1: »The reference values (often just references) are pointers«
- one thinks of a subset of C++ without »reference parameters«
- one thinks of a subset of C++, that does not allow all objects as arguments, but only pointers and some primitiv objects.
Therefor, the situation in Java is a special case of the situation in C++.
>So this leaves only two ways of returning things from methods. >1) the method's returned type. >2) Pass in a reference to a Data holder object, One also might pass in a »continuation«.
For example, someone might want to return a pair. This (»getPair«) is not valid Java:
class Server { static java.util.Random rand = new java.util.Random(); static( int, int )getPair( final Client client ) { return( rand.nextInt( 11 ), rand.nextInt( 21 )); }}
class Example { public void main() { java.lang.System.out.println( Server.getPair( this )); }}
Instead, one might pass in the »continuation«:
interface Client { void continuation( int x, int y ); }
class Server { static java.util.Random rand = new java.util.Random(); static void getPair( final Client client ) { client.continuation( rand.nextInt( 11 ), rand.nextInt( 21 )); }}
class Example implements Client { public void continuation( final int x, final int y ) { java.lang.System.out.println( x + ", " + y ); } public void main() { Server.getPair( this ); }}
public class Main { public static void main( final java.lang.String[] args ) { new Example().main(); }}
Here, the server does not »return« the int-pair to the calling method, but to the calling object (to the method »continuation«), insofar the word »return« could still be used.
Adam Warner - 19 Apr 2006 03:23 GMT > Here's a model of my problem. I see what's causing it (thinking in C++, > with pass by reference) but I don't see a Java way to solve it. Probably [quoted text clipped - 5 lines] > method2, relying on method2 to process the string array and return a > String and a new String array for further processing back to method1. What you're actually after is multiple return values ("return a String and a new String array" => two return values). Unfortunately Java provides no efficient construct for multiple return values. They were removed before the first release of Java. I'd like to locate the 1997 interview referenced in this Usenet post: <http://groups.google.com/group/comp.object/msg/580bc83193356221?dmode=source>
Gosling may just be being folksy, but I find the following answers from a language designer as really poor:
G:> Yeah. I actually had an implementation of Mesa-style G:> multiple value returns mostly implemented just before G:> 1.0 that I ripped out because I was uncomfortable with G:> the semantics, and the schedule was getting tight. G:> Right now, I really regret it.
The next thing you may try to do with a language that doesn't implement multiple return values is pass the values upon the stack via a pointer. This will be less efficient than multiple return values passed in registers but more efficient than heap-allocated multiple return values. This approach is also impossible in Java.
This leaves building an object to pass the values in. You have the choice of whether the caller should pass an object for the callee to mutate or whether the callee builds a fresh object to hold the multiple return values.
You may be inclined to pass a container object to the callee to mutate since the container can be reused by the caller. I suggest the callee should build a new container for multiple values each time because the most sophisticated JVMs will soon be able to avoid heap allocation (and perhaps even stack allocation) via escape analysis. Thus we are close to the point where Gosling's omission of explicit multiple return values will no longer be a hindrance to efficiently returning multiple values. While your code is likely to run slower in the meantime it's the future-proof option.
Regards, Adam
Domagoj Klepac - 19 Apr 2006 20:46 GMT >This leaves building an object to pass the values in. You have the choice >of whether the caller should pass an object for the callee to mutate or [quoted text clipped - 10 lines] >your code is likely to run slower in the meantime it's the future-proof >option. Wait a second.
Are you saying that in a future JVMs objects won't be "passed by reference", and that a program like this:
public class PassByReference { public static void appendFive(StringBuffer s1, StringBuffer s2) { s1.append(" two"); s2.append(" three"); }
public static void main(String[] args) { StringBuffer str = new StringBuffer("one"); PassByReference.appendFive(str, str); System.out.println(str); } }
...won't print "one two three" but "one"?
Of course, using Strings or primitives in this example would print "one", and this is a bit "dirty", and is not something I'd recommend as a best practice, and ESPECIALLY to a C++ programmer :)))... but it is a useful hack in a few rare cases, where it can save you from creating a class just to return several objects.
Domchi
Adam Warner - 20 Apr 2006 01:14 GMT >>This leaves building an object to pass the values in. You have the >>choice of whether the caller should pass an object for the callee to [quoted text clipped - 15 lines] > Are you saying that in a future JVMs objects won't be "passed by > reference", and that a program like this: [...]
> ...won't print "one two three" but "one"? Optimisations via escape analysis must not change existing program semantics. What it will mean is that objects treated like primitives (where object identity is irrelevant) will likely be as fast as if the JVM had explicit support for those primitives.
A great example is an immutable complex number class. To return an immutable complex number one returns a new complex object. In the vast majority of cases the object's existence is fleeting. All the calling code does is extract the real and imaginary fields before losing its object reference. Current the Sun JVM always builds the complex object and this increases garbage collection activity and reduces locality of reference. Soon the Sun JVM will be able to deduce that it doesn't need to create the object. Behind the scenes it will get those fields to the caller via a different route. This route may be the stack or machine registers.
From another perspective this complex number is the return of TWO values. These optimisations will also apply to the return of conceptually discrete multiple values that are wrapped up in a single return object.
Whenever you hold onto a return object past the caller's lifetime it will continue to be heap allocated to preserve program semantics.
> ... but it is a useful hack in a few rare cases, where it can save you > from creating a class just to return several objects. Without an additional syntactic blessing from Sun you'll have to explicitly wrap up and extract multiple return values.
These optimisations aren't just necessary for a few rare cases. They are critical for high performance object-oriented operation upon primitive-style objects.
The remaining critical omission from the JVM is a new set of arrays with 64-bit (long) array indices. This will not affect existing code as long array indices are currently a compile time error. While easy to implement and critical for some scientific computing tasks I do not see Sun taking leadership of this issue. My guess is (an offshoot of) the Harmony project will eventually force Sun to address this.
Regards, Adam
Oliver Wong - 20 Apr 2006 15:49 GMT >>This leaves building an object to pass the values in. You have the choice >>of whether the caller should pass an object for the callee to mutate or [quoted text clipped - 30 lines] > > ...won't print "one two three" but "one"? Obviously, Sun "owns" Java, and they are free do whatever they want with future versions of the language.
But I think what Adam is saying is that the language and JVM specifications specify behaviour, not implementation. As long as the above program prints "one two three", it doesn't matter HOW it does it. It might do so by passing stuff around by value, or passing stuff around by reference, or by not passing anything around at all and doing some inlining magic instead, or maybe something else I haven't come up with yet.
- Oliver
Lenny Wintfeld - 19 Apr 2006 04:40 GMT Thanks very much for your comments and advice. It's been a long day (about 12 straight hours coding) so I'll try using your suggested "reply class" or "return class" tomorrow. Though I can't imagine having trouble with that method.
If there's any trouble (which I doubt) I'll post it back on this thread.
Thanks again
Lenny Wintfeld
> Here's a model of my problem. I see what's causing it (thinking in C++, > with pass by reference) but I don't see a Java way to solve it. Probably [quoted text clipped - 44 lines] > back lots of stuff created in one method to it's caller? Feel free to > hack up the above example to show me how, if it's convenient. Luc The Perverse - 19 Apr 2006 05:09 GMT > Thanks very much for your comments and advice. It's been a long day > (about 12 straight hours coding) so I'll try using your suggested "reply [quoted text clipped - 6 lines] > > Lenny Wintfeld Remember to keep a positive attitude. Remember, this is not an annoying workaround to fix a problem with the language - but rather simply the way the language is meant to be used ;)
I imagine that you will find, as I did, that you will find this generally helps you to make cleaner more readable code.
-- LTP
:) Dimitri Maziuk - 19 Apr 2006 21:40 GMT Lenny Wintfeld sez:
> Here's a model of my problem. I see what's causing it (thinking in C++, > with pass by reference) but I don't see a Java way to solve it. Probably > because I'm so new to Java. Yes, but Java's fscked-up terminology takes large portion of the blame. In Java "reference values" are pointers passed by value. Assignment operator simply re-points the pointer (not like C++ references), whereas dereferencing operator does what you expect: e.g. you can modify the object by calling its mutator methods (not like "pass by value").
> Here's the problem. > [quoted text clipped - 7 lines] > String [] sa1 = null; // to be created and returned by method2 > String s2 = null; // to be created and returned by method2 Simple for sa1: String [] sa1 = new String[N]; (provided N is known beforehand, otherwise use ArrayList). For s2 the trick is that strings are "immutable" (have no mutator methods), so you need to use a mutable version: StringBuilder (1.5+ only) or StringBuffer: StringBuilder s2 = new StringBuilder();
Creating a separate class for return value only makes sense where you'd return a struct in C++. In this case I'd probably not bother anyway : you can as easily return s2 as 1st element of your array.
Dima
 Signature The speed at which a mistyped command executes is directly proportional to the amount of damage done. -- Joe Zeff
Stefan Ram - 19 Apr 2006 21:52 GMT >Yes, but Java's fscked-up terminology takes large portion of the What does this have to do with fsck?
http://en.wikipedia.org/wiki/Fsck
Stefan Ram - 19 Apr 2006 22:01 GMT >>Yes, but Java's fscked-up terminology takes large portion of the >What does this have to do with fsck? >http://en.wikipedia.org/wiki/Fsck Since by now I actually have read the explanation on this page, no answer is necessary anymore.
Scott.R.Lemke@gmail.com - 20 Apr 2006 16:02 GMT Why not just pass is back as the return value? You are using your return value's right now for error handling(I am assuming that's what the true/false return would be used for). Java and C++ have this wonderful thing called Exceptions. If you look at your false return situations and think "Hey, this would only return false in a few exceptional cases", then you have an exception. If you have a situation where you think "Hey, this is going to return false a lot under normal circumstances", then you need to refactor.
Dimitri Maziuk - 20 Apr 2006 19:27 GMT Scott.R.Lemke@gmail.com sez:
> Why not just pass is back as the return value? You are using your > return value's right now for error handling(I am assuming that's what > the true/false return would be used for). Java and C++ have this > wonderful thing called Exceptions. Which, as the name implies, are used to signal "exceptional" circumstances. There's nothing in OP to indicate that false return from his method is out of the ordinary and warrants a jump to clean-up/recovery block.
Dima
 Signature All whitespace is equivalent except in certain situations -- ANSI C standard committee
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|