Wednesday, February 2, 2011

Chapter 17: Passing Variables into Methods

In most of the examples in the preceding chapters, we have used methods and invariably they would’ve had arguments passed into them. This chapter will deal with the details and nuances of passing values into methods.

To understand this chapter better, you’ll first need to be comfortable with the literals & assignments chapter which incidentally happens to be the previous chapter. “Click Here” to visit the chapter if you want to refresh it.

Passing Object Reference Variables

When you pass an object variable into a method, you must keep in mind that you’re passing the object reference, and not the actual object itself. Remember that a reference variable holds bits that represent (to the underlying VM) a way to get to a specific object in memory (on the heap). More importantly, you must remember that you aren’t even passing the actual reference variable, but rather a copy of the reference variable. A copy of a variable means you get a copy of the bits in that variable, so when you pass a reference variable, you’re passing a copy of the bits representing how to get to a specific object. In other words, both the caller and the called method will now have identical copies of the reference, and thus both will refer to the same exact (not a copy) object on the heap.

For this example, we’ll use the TestDimension class. For now assume that this TestDimension class is a simple extension of the java.awt.Dimension class.
1. import com.test.TestDimension;
2. class ReferenceTest {
3. public static void main (String [] args) {
4. TestDimension d = new TestDimension(15,20);
5. ReferenceTest rt = new ReferenceTest();
6. System.out.println("Before alter() d.height = "
+ d.height);
7. rt.alter(d);
8. System.out.println("After alter() d.height = "
+ d.height);
9. }
10. void alter(TestDimension dim) {
11. dim.height = dim.height + 1;
12. System.out.println("dim = " + dim.height);
13. }
14. }

When we run this class, we can see that the alter() method was indeed able to alter the original TestDimension object created on line 4.
C:\Java Projects\Reference>java ReferenceTest
Before alter() d.height = 20
dim = 11
After alter() d.height = 21

Notice when the TestDimension object on line 4 is passed to the alter() method, any changes to the object that occur inside the method are being made to the object whose reference was passed. In the preceding example, reference variables d and dim both point to the same object.

Does Java Use Pass-By-Value Semantics?

If Java passes objects by passing the reference variable instead, does that mean Java uses pass-by-reference for objects? Not exactly, although you’ll often hear and read that it does. Java is actually pass-by-value for all variables running within a single VM. Pass-by-value means pass-by-variable-value. And that means, pass-by-copy-of-the-variable!

It makes no difference if you’re passing primitive or reference variables, you are always passing a copy of the bits in the variable. So for a primitive variable, you’re passing a copy of the bits representing the value. For example, if you pass an int variable with the value of 3, you’re passing a copy of the bits representing 3. The called method then gets its own copy of the value, to do with it what it likes.
And if you’re passing an object reference variable, you’re passing a copy of the bits representing the reference to an object. The called method then gets its own copy of the reference variable, to do whatever it likes with it. But because two identical reference variables refer to the exact same object, if the called method modifies the object, the caller will see that the object the caller’s original variable refers to has also been changed. This is exactly why in the preceding example the values of the object were modified when the alter() method was invoked.

The bottom line on pass-by-value: the called method can’t change the caller’s variable, although for object reference variables, the called method can change the object the variable referred to. What’s the difference between changing the variable and changing the object? For object references, it means the called method can’t reassign the caller’s original reference variable and make it refer to a different object, or null. For example, in the following code fragment,
void car() {
Ferrari f = new Ferrari();
drive(f);
}
void drive(Ferrari g) {
g.setName("Vroooommmmm");
g = new Ferrari();
}

reassigning g does not reassign f! At the end of the car() method, two Ferrari objects have been created, one referenced by the local variable f and one referenced by the local (argument) variable g. Because the drive() method has a copy of the reference variable, it has a way to get to the original Ferrari object, for instance to call the setName() method. But, the drive() method does not have a way to get to the f reference variable. So drive() can change values within the object f refers to, but drive() can’t change the actual contents (bit pattern) of f. In other words, drive() can change the state of the object that f refers to, but it can’t make f refer to a different object!

Passing Primitive Variables

Let’s look at what happens when a primitive variable is passed to a method:
class PrimitiveReferenceTest {
public static void main (String [] args) {
int a = 1;
PrimitiveReferenceTest rt = new PrimitiveReferenceTest();
System.out.println("Before alter() a = " + a);
rt.alter(a);
System.out.println("After alter() a = " + a);
}
void alter(int number) {
number = number + 1;
System.out.println("number = " + number);
}
}

In this simple program, the variable a is passed to a method called alter(), which increments the variable by 1. The resulting output looks like this:
Before alter() a = 1
number = 2
After alter() a = 1

Notice that the value of ‘a’ did not change after it was passed to the method. Remember, it was a copy of a that was passed to the method. When a primitive variable is passed to a method, it is passed by value, which means pass-by-copy-of-the-bits-in-the-variable.

Shadowing Variables:

Shadowing involves redeclaring a variable that’s already been declared somewhere else (In the lines of code before the current line).
The effect of shadowing is to hide the previously declared variable in such a way that it may look as though you’re using the hidden variable, but you’re actually using the shadowing variable. You might find reasons to shadow a variable intentionally, but typically it happens by accident and causes hard-to-find bugs. On the exam, you can expect to see questions where shadowing plays a role.

You can shadow an instance variable by declaring a local variable of the same name, either directly or as part of an argument:
class Ferrari {
static int size = 7;
static void changeIt(int size) {
size = size + 200;
System.out.println("size in changeIt is " + size);
}
public static void main (String [] args) {
Ferrari f = new Ferrari();
System.out.println("size = " + size);
changeIt(size);
System.out.println("size after changeIt is " + size);
}
}

The preceding code appears to change the size instance variable in the changeIt() method, but because changeIt() has a parameter named size, the local size variable is modified while the instance variable size is untouched. Running class Ferrari prints
%java Ferrari
size = 7
size in changeIt is 207
size after changeIt is 7

Things become more interesting when the shadowed variable is an object reference, rather than a primitive:
class Car {
int carNum = 28;
}
class Ferrari {
Car myCar = new Car();
void changeIt(Car myCar) {
myCar.carNum = 99;
System.out.println("myCar.carNum in changeIt is " + myCar.carNum);
myCar = new Car();
myCar.carNum = 420;
System.out.println("myCar.carNum in changeIt is now " + myCar.carNum);
}
public static void main (String [] args) {
Ferrari f = new Ferrari();
System.out.println("f.myCar.carNum is " + f.myCar.carNum);
f.changeIt(f.myCar);
System.out.println("f.myCar.carNum after changeIt is "
+ f.myCar.carNum);
}
}

The preceding code prints out this:
f.myCar.carNum is 28
myCar.carNum in changeIt is 99
myCar.carNum in changeIt is now 420
f.myCar.carNum after changeIt is 99

You can see that the shadowing variable (the local parameter myCar in changeIt()) can still affect the myCar instance variable, because the myCar parameter receives a reference to the same Car object. But when the local myCar is reassigned a new Car object, which we then alter by changing its carNum value, Ferrari’s original myCar instance variable is untouched.

Previous Chapter: Chapter 16 - Literals, Assignments and Variables

Next Chapter: chapter 18: Arrays

2 comments:

  1. Very confusing post.You are contradicting yourself sometimes.

    ReplyDelete

© 2013 by www.inheritingjava.blogspot.com. All rights reserved. No part of this blog or its contents may be reproduced or transmitted in any form or by any means, electronic, mechanical, photocopying, recording, or otherwise, without prior written permission of the Author.

ShareThis

Google+ Followers

Followers