views:

109

answers:

8

Hi,

I actually thought that I had a good idea of how passing values in Java actually work, since that was part of the SCJP cert which I have passed. That was until today, when I at work discovered a method like this:

public void toCommand(Stringbuffer buf) {

  buf.append("blablabla");
}

Then the caller of that method used the function like this:

StringBuffer buf = new StringBuffer();
toCommand(buf);
String str = buf.toString();

Now I thought that that code would give str the value "", but it actually give it the value from the mehod. How is this possible? I thought things didnt work like that in Java?

Either way... it should be considered a bad practice to write code like this in Java, right? Because I can imagine it can bring some confusion with it.

I actually spent some time searching on this, but my interpretation of what these sources are saying, is that it shouldnt work. What am I missing?

http://www.yoda.arachsys.com/java/passing.html
http://javadude.com/articles/passbyvalue.htm

Sebastian

A: 

str has the value "blablabla" because a reference to the StringBuilder instance is passed to toCommand().

There is only one StringBuffer instance created here - and you pass a reference to it to the toCommand() method. Therefore any methods invoked on the StringBuffer in the toCommand() method are invoked on the same instance of the StringBuffer in the calling method.

matt b
+4  A: 

StringBuffer is mutable. The toCommand() method gets a reference to that objects which is passed by value (the reference), the reference allows the method to change the mutable StringBuffer.

If you are thinking about why we cannot do this with String it is because String is immutable in which case it will result in the creation of another String object and not having the changes reflected in the object of which the reference is passed.

And I don't see why it should be bad practice.

Bakkal
+10  A: 
T.J. Crowder
The author of one of the articles I posted says that since it is references that are copied and not actually objects, performance isnt an issue.
@sebastianlarsson: It's true that the smaller the amount of data you're moving around, the faster things will tend to be. I wouldn't call it a *central* point, but it's true nevertheless.
T.J. Crowder
Great example. I have edited the code so that it sets `buf2 = buf1` *before* it appends to `buf1`. I hope that is OK and that it makes it even more explicit exactly what's going on. i.e. `buf2` is not a copy of the `buf1` object.
mikej
@mikej: Thanks for doing that. I wasn't sure about it at first, but the more I thought about it, the smarter it was. :-)
T.J. Crowder
+1 for the buf1, buf2 example and ascii art
naikus
A: 

It is not always bad practise. Consider the task to assemble an email, then you could do something like:

 StringBuilder emailBuilder = new StringBuilder();
 createHeader(emailBuilder);
 createBody(emailBuilder);
 createFooter(emailBuilder);
 sendEmail(emailBuilder.toString());

It surely could be used to create confusion and for public API one should add a note or two to the javadoc, if the value at the passed reference is changed.

Another prominent example from the Java API:

Collections.sort(list);

And as other already explained and just to complete the answer: The value of the reference of the StringBuffer (<-- start using StringBuilder instead!) is passed to toCommand so outside and inside the toCommand method you access the same StringBuffer instance.

Andreas_D
The code I noticed this in was actually in a project using threads, so there might actually be a reason for them using StringBuffer.
Could be - or maybe it was originally written for Java 1.4.2 or less as StringBuilder was introduced with Java 1.5
Andreas_D
A: 

Since you get a reference to the instance you can call all the methods on it, but you can't assign it to anything else.

public void toCommand(Stringbuffer buf) {
    buf.append("blablabla"); // okay
    buf = new Stringbuffer(); // no "effect" outside this method
}
Patrick
And here is what I believe the reason why I thought objects couldnt be altered in a method. Creating a new StringBuffer in that method only changes which object "buf" (the copy of the reference to the original object) refers to. Thx all now I understand!!
@sebastianlarsson: Exactly, you got it
Patrick
A: 

When you pass an object to a method, unlike in C++, only the reference to the object gets copied. So when a mutable object is passed to the method, you can change it and the change gets reflected in the calling method. In case you're too much into C/C++ programming, you should know that in java, pass by reference(in C++ lingo) is the default(and the only) way of passing arguments to methods.

Jagat
A: 

This is because the in the method, when you pass an object, a copy of a reference to the object is passed by value. Consider the example below:

    public class Test {

     public static void modifyBuff(StringBuffer b)   {
        b.append("foo");
     }

     public static void tryToNullifyBuff(StringBuffer b)   {
        b = null; // this will not affect the original reference since 
                  // the once passed (by value) is a copy
     }


     public static void main(String[] args) {
        StringBuffer buff = new StringBuffer();  // buff is a reference 
                                                 // to StringBuffer object

        modifyBuff(buff);

        System.out.println(buff); // will print "foo"

        tryToNullifyBuff(buff); // this has no effect on the original reference 'buff'

        System.out.println(buff); // will still print "foo" because a copy of 
                                  // reference buff is passed to tryToNullifyBuff() 
                                  // which is made to reference null 
                                 // inside the method leaving the 'buff' reference intact
     }
}

This can be done with other mutable objects like Collection classes for example. And this is not at all a bad practice, in fact certain designs actively use this pattern.

naikus
A: 

Just in response to some of the comments about the SCJP (sorry don't have permissions to leave comments - apologies if leaving an answer is not the right way to do this).

In defence of the SCJP exam, passing object references as method parameters AND the differences between immutable String objects and StringBuffer / StringBuilder objects are part of the exam - see sections 3.1 and 7.3 here:

http://www.javadeveloper.co.in/scjp/scjp-exam-objectives.html

Both topics are covered quite extensively in the Kathy Sierra / Bert Bates study guide to the exam (which is the de facto official study guide to the exam).

dairemac