Question

I was going through this excellent article on Java reference semantics by Jon Skeet, where he states that

We assume the presence of a procedure named f that takes a formal parameter s. We call that function giving it an actual parameter g.

The calling code:

         f( g )

The function:

   procedure f( s )
   begin
     -- body of the procedure
   end;

All object instances in Java are allocated on the heap and can only be accessed through object references. So if I have the following:

       StringBuffer g = new StringBuffer( "Hello" );

The variable g does not contain the string "Hello", it contains a reference (or pointer) to an object instance that contains the string "Hello".

So if I then call f( g ), f is free to modify its formal parameter s to make it point to another StringBuffer or to set it to null. The function f could also modify the StringBuffer by appending " World" for instance. While this changes the value of that StringBuffer, the value of that StringBuffer is NOT the value of the actual parameter g.

my understanding could be wrong. the program below does change the Stringbuffer passed to the method

public class MutabilityStringBuffer {
    public static void main(String[] args){
        StringBuffer sb = new StringBuffer("hello");
        System.out.println("String before append: "+ sb.toString());
        addString(sb);
        System.out.println("Sting after append "+ sb.toString());

        String s = "hello";
        System.out.println("String before append: "+ s);
        addString(s);
        System.out.println("Sting after append "+ s);

    }
    public static void addString(StringBuffer word){
        word.append(" world!");
    }
    public static void addString(String word){
        word+=" world!";
    }

}

ofcourse, Jon Skeet could not be wrong. But I see that the Stringbuffer can be changed by passing it to method, because stringbuffer is mutable, which is a bit contradictory to what Skeet posted. please clear my understanding here.

Thanks

Was it helpful?

Solution

See comments

StringBuffer sb = new StringBuffer("hello"); // sb holds reference 
System.out.println("String before append: "+ sb.toString()); // you print the value 
addString(sb);  // you use the reference to append to the StringBuffer
System.out.println("Sting after append "+ sb.toString()); // you print the value

String s = "hello"; // s holds a refernece
System.out.println("String before append: "+ s); // you print its value
addString(s); // // the word variable would hold a new reference inside the method 
System.out.println("Sting after append "+ s); // you print its value

In here

public static void addString(String word){
    word+=" world!";
}

The original value of the reference passed to word changes when you reassign it with

word+=" world!"; 

It goes something like this

String word = [copy of value of the argument's reference];
word = word.toString() /* toString() is unnecessary, but just to make the point */ + " world";

where the result of String concatenation is a new String object and therefore a new reference.

In the following

public static void addString(StringBuffer word){
    word.append(" world!");
}

you access the object referenced by word, call a method on that object which internally modifies a char[]. So you've changed the value of the object, not the value of the reference. Changing the reference would look like

public static void addString(StringBuffer word){
    word = new StringBuffer("Answer to the Ultimate Question of Life, the Universe, and Everything: ");
    word.append("42"); 
}

The append is performed on a new StringBuffer object, not the one you passed as an argument, proof that the objects are passed by value.

OTHER TIPS

This has been discussed to death. Anyway, given some idle time, here is my response. It is the same response I always give, and it links to the same wikipedia article on evaluation strategies I always link to.


I find that using the term Call by (Object) Sharing removes most ambiguities when describing the semantic behavior of passing objects as arguments in Java (and many other languages including Python, C#, etc). Doing so clearly identifies any [im]mutability considerations. That is, the object accessed inside the method is the same object as outside! No new object has been created, copied, cloned or otherwise duplicated during this calling/evaluation strategy - so any mutable change made the object affects the same "shared" object.

Passing the reference-of an object an object using Call by Value is an implementation detail, albeit one that is discussed in depth in the Java Language Specification (JLS). In the case of Java it would be more "accurate" to say Call by Value of the Reference-to the Object - but this is still implemented-as Call by Value. (Some languages like ECMAScript, however, achieve the exact same Call by Sharing semantics without discussing any such implementation details.)

To avoid confusion, I use Call by Reference only to mean that the called function can modify variable bindings (which are orthogonal to object mutability) of the caller through re-assignment to parameters. Unfortunately Call by Reference has been given alternative - and confusing - meanings in some language circles.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top