discussing some technical aspects covering tools, frameworks, technologies etc (the areas where i m involved and getting experienced) - feel free to discuss!

Thursday, September 13, 2007

Pass by Reference for object references holds good really?


Pre-requisite: Well, If you want to refreshwith the concepts of "Pass-by-value Vs Pass-by-reference", you may please refer this previous entry in this blog.

With respect to Java, it "passes everything by value". I repeat, "everything" by value only.

Generally, we may think that passing by values will hold good only for primitive types and values but not for the object references.

This everything includes even the references to objects.

Lets say, you have a method which takes an object as a parameter as follows


public void doChange(MyClass myClassObj)
{
//do something..
}



And you call that method from some other piece of code as follows

//calling method
public void method1()
{
MyClass myClassObj = new MyClass();
doChange(myClassObj);
}


The reference variable myClassObj is being assigned to an object of class MyClass in Heap. Just like primitive variables holding bit patterns to represent a value, reference variables also store the bit patterns to reach an object.

Once you call the method doChange() by passing the reference variable "myClassObj" to it, the value of the bit patterns held by "myClassObj" is being copied and passed into the called method doChange(). In the called method doChange(), the copied-and-sent bit pattern is received in the same name as that of the original reference variable "myClassObj". But the compiler treats that as a different one just like primitives.

Lets say the received reference variable as "myClassObjLocal" for easy understanding. In this stage, both the original refernece variable "myClassObj" and the received reference variable inside the doChange() method "myClassObjLocal" (to compiler) point to the same object in Heap.

Now if you change the state of the object being pointed by, it will reflect to both the reference variables because both of them point to the same object in Heap. Whereas,if you change the received reference variable "myClassObjLocal" to point to a new object (reassign a different object), it does NOT reflect back to the original object because the bit pattern to the "myClassObjLocal" alone gets changed!

If you are clear with the above paragraphs, lets go to an example to make it more clear.

Lets take a small example.

class TestObjRef
{
int intValue;

public TestObjRef()
{
intValue = 1;
}

public static void changeStateOfObject(TestObjRef obj)
{
obj.intValue = 2;
}

public static void changeReference(TestObjRef obj)
{
obj = new TestObjRef();
obj.intValue = 9;
}

public static void main(String[] args)
{
TestObjRef obj1 = new TestObjRef();
System.out.println("obj1.intValue (1) = "+obj1.intValue);
changeStateOfObject(obj1);
System.out.println("obj1.intValue (2) = "+obj1.intValue);
changeReference(obj1);
System.out.println("obj1.intValue (3) = "+obj1.intValue);
}
}


Running the above code produces the following output:

Output:
obj1.intValue (1) = 1
obj1.intValue (2) = 2
obj1.intValue (3) = 2


changeStateOfObject Method

This is because, changeStateOfObject() method just changes the value of the variable "intValue" which definitely constitutes the state of object. And as such, both the "obj1" in main() method and "obj" in changeStateOfObject() method point to the same object. Means, they both hold the same bit patterns to reach a single object of TestObjRef class in heap.

That's why the output in the second line shows the changed state of object "2" as the value of "intValue" property.

changeReference() Method

If you look at the changeReference() method, by the time of receiving the argument, both the "obj1" reference variable in main() method and received reference variable "obj" in changeReference() method both hold the same bit patterns to reach the same and single TestObjRef class in Heap.

But inside the method, the received "obj" reference variable is reassigned to a newly created object of TestObjRef class. In this case, only the bit pattern of the "obj" reference variable inside the changeReference() method is changed and the change is NOT reflected back to the originally sent reference variable "obj1".

That's why the third line in the output still shows the value of "intValue" as "2" since the changed value "9" is only reflected in the received local reference variable "obj" in changeReference() method.


Note :
The received argument (object reference variable) is treated local to the method. That means, the scope of the variable is only local to the called method just like primitives and the variable cannot be accessed outside the method.

Pass by value Vs Pass by Reference

We all would have read the two terms "pass-by-value" and "pass-by-reference" especially when you deal with methods (which are also called as functions).

Let's just have a quick glance at both the terms with a small example.

The definition of these terms goes as follows.

Pass-by-value: A copy of the value is being passed from the calling method to the called method.

What exactly do you mean by copy of the value? In general, we tend to pass the variable for the value to be passed. In such case, whatever the value the variable holds is being copied and passed across. That means, you are taking a xerox of the o riginal value and pass the xeroxed copy to the called method.

What are the effects?
Since you have only passed the xeroxed copy and NOT the original value, whatever changes you do in the called method, the change is not reflected back to the original variable.

A Sample program for pass-by-value:

void changeValue(int a)
{
int b = ++a;
printf("changeValue -> a, b contain: %d,%d",a,b);
}

int changeAndReturn(int a)
{
int b = ++a;
printf("changeAndReturn -> a,b contain : %d,%d",a,b);
return b;
}

int main()
{
int i = 1;
changeValue(i);
j = i;
printf("\ni and j is : %d,%d\n",i,j);
printf("\ncalling changeAndReturn()..");
changeAndReturn(i);
j = i;
printf("\nNow i and j contain: %d,%d",i,j);
}


Output:
changeValue-> a, b contain: 2 2
i and j is: 1,1
calling changeAndReturn()..
changeAndReturn -> a,b contain: 2 2
Now i and j contain: 1 2


Explanation:
Let's see how exactly the values are dealt. In the main() method, you are calling changeValue() method as

changeValue(i);

At this time, the value of i (which is 1) is being copied (Remember! xeroxed) and the xeroxed copy is what getting passed to the changeValue() method. Inside the changeValue() method, you get (receive) the passed value in the variable name "a". Note: Even if you have received the passed argument as the same name "i" in the function, the compiler would treat both of them differently!

Soon after you take a xerox copy of the value of the variable and use it for calling functions, the connection between the copied value and the original value is cut. There is no relation between those two values and /or variables.

Inside the method changeValue(), we do the following steps:

(i) receive the value passed in the variable whose name is 'a' (implicit)
(ii) declare an int variable named 'b'
(iii) increment the received value in its variable 'a' and assign the incremented value to 'b'

Since because the original value ('i' in main) does NOT hold any relationship with its xeroxcopy ('a' here), the changes you do in the called function (incrementing the value in changeValue()) does NOT reflect back.

That's why in the main after the function call returned, the printf statement prints the value both i and j contain 0. Once the function call is returned, the present focus is in main() function and now we are assigning the value of 'i' to 'j' which is 0 only. This clearly shows that there is NO effect on the variable 'i' even after calling the function changeValue() which does some change to it actually.

Just to have an effect, we have one more method called changeAndReturn() which changes the value and returns it. In the calling function (main()), we get the returned value and assign it to 'j'. That's why the second set of output shows the incremented value '2' for both 'i' and 'j'.

Pass-by-reference: The memory address of the variable is being passed instead of making a copy of the value. This holds good for C, C++ languages (as far as i know as such).

What are the effects?
Since you pass the memory address itself to the function and you obtain the value from the received memory address inside the called function, you get the full control over the value. That means, whatever change you do, it will be reflecting in the original variable in the calling function eventhough you do NOT return either the memory address or value back.

It is because, you have played around with the memory directly instead of values and in both the calling and called functions, the values are obtained and manipulated with respect to memory only. So the change is reflected back.

Let's see a sample program for pass-by-reference:

Sample Program for pass-by-reference:

void changeValue(int *int_ptr)
{
//doing an increment operation on the 'dereferenced' pointer
int b = ++(*int_ptr);
printf("changeValue -> a, b contain: %d,%d",a,b);
}

int main()
{
int i = 1;
int *int_ptr = &i;
printf("\ncalling changeValue()..");

//Note: we are passing the pointer to the function! (the 'address')
changeValue(int_ptr);
//assigning the value of 'i' to 'j'
int j = i;
printf("\ni and j is : %d,%d\n",i,j);
}


Output:
changeValue-> a, b contain: 2 2

calling changeValue()..
i and j is: 2,2


Explanation:

If you carefully look at the implementation and way of calling the function changeValue(), it actually receives a pointer to an integer! that means, we are passing the 'memory address' of an integer variable.

Inside the function, we do the following steps:

(i) receive the address of the integer variable in the name 'int_ptr' (the same name as in the calling function, here main(). But remember! for the compiler, they both are different!)
(ii) we declare an integer variable 'b'
(iii) we dereference the pointer variable ('dereferencing' means obtaining the value present in the location where the pointer is pointing to)
(iv) we increment the value being obtained by the dereferencing operation
Note: here the value is incremented directly in the memory location
(v) and assign the incremented value to the variable 'b'

One important thing is that, we do NOT return any value back to the calling function. But the changes happend in the memory location directly in the called function (changeValue()). Once the control comes back to the calling function (main()), we are still accessing the value in the same memory location through the original variable 'i' and assigning to 'j'.

That's why we see both i and j having the value '2'.


About Me

ஏதோ பிறந்தோம், ஏதோ வாழ்ந்தோம் என்றிருப்பதல்ல வாழ்க்கை! எப்படி வாழ்ந்தோம் என்பதும் ஒரு அங்கம். வாழக் கிடைத்த வாழ்க்கையில், ஒரு சிலருக்காவது வசந்தத்தின் முகவரியை அறிமுகம் செய்தோமேயானால் அதுவே வசீகரத்தின் வனப்பைக் கூட்டும்!