Friday, August 8, 2008

Reference Types: Names and Objects

When we learned about primitive variables we learned that they are a way of storing a simple value, like a number or a true-false state. We give them a name, and whenever we want to get that value we use that name. Whenever we want to change the value that's stored, we set the name equal to the new value:
int value;     //Declare an integer variable named value.
value=100; // Store the number 100 in value.
System.out.println(value); // Print the number in value, "100"
value=20; // Put 20 in value. Over-writes the 100.
count=value; // Gets the number from value and puts
// it in count.
// Assumes 'count' was declared as an int before.

When we use objects, we're using a more complex kind of variable. We don't do it for the sake of complexity, but because adding some complexity here makes things a lot simpler elsewhere in our program. A lot simpler.

Object variables are called "reference types", the type is actually the class that the object belongs to. The difference between a reference variable and a primitive variable is that a primitive variable actually is a storage location in computer memory that holds the stored information. A reference variable is a name that names a specific type of information. It doesn't actually hold the information, it's just associated with an object that actually holds the information.

This why there's a two step process in creating a Java variable. The first step, declaration, sets up a name we're going to use for a particular type of object. The second step, initialization, associates that name with an object--creating a new object if necessary.

Let's say I'm talking to a friend. I say, "I'm going to get a hamster and call it Wilmot."

"That's nice," says the friend. "Can I see Wilmot?"

"I don't have him yet."

"What color is Wilmot?"

"I don't know, I don't have a hamster yet."

Then I go to the pet store and pick up a hamster. I call him Wilmot, put him in a cage and take him home. I've now initialized the name Wilmot I declared to my friend earlier to point to a specific hamster.

Now when my friend says "Can I see Wilmot?" I can show him a hamster, and say "This is Wilmot." When he wants to know the color, there is an object to get the color of.

In Java terms, if I had a Hamster class, I could declare a name like this:
Hamster wilmot;

I now have a name for a Hamster object, but no Hamster. I can initialize the reference variable by getting a new Hamster object using the class's constructor, Hamster():
wilmot=new Hamster();

Now I have a Hamster object associated with the name. The name references the Hamster object that's been created. Hence the term "reference variable".

Now, if I create a new name and make it point to the same Hamster object, it becomes a new name for the same object:
Hamster fluffy;

Now, if I make any changes to fluffy they also happen to wilmot. For example, if wilmot's size is 10cm, and I set fluffy's size to 12cm, then get wilmot's size it will be 12cm.

This is different from what happens with primitive variables. If I do the same thing with a pair of primitive variables like this:
int value, count;
value=100; // Initialize value to 100;
count=value; // Set count equal to value.
count=count-20; // Subtract 20 from count.
// We could have said 'count-=20;', too.
System.out.println("value= " + value); // Print value.
System.out.println("count= " + count); // Print count.

Then the output will be:
value= 100
count= 80

With a primitive variable, when we set it equal to another primitive we get a copy of the value in that primitive's own storage. When we set a reference variable equal to another reference variable, we are now referring to the same object as the other reference variable. In pet terms, it's like giving our hamster a second name. We might call it wilmot or fluffy, but they're both the same hamster.

If we need to have each name apply to a different hamster, we need to go back to the pet store for another hamster. In Java, we need to use the constructor to get another Hamster object:
Hamster wilmot, fluffy;
wilmot=new Hamster();
fluffy=wilmot; // fluffy and wilmot are now both names
// for the same Hamster object. Anything we do to fluffy
// will also apply to wilmot, and vice versa.
fluffy=new Hamster();
// Now fluffy applies to a different Hamster object.
// We can make changes to fluffy and
// they won't affect wilmot. We now have two
// Hamster objects, each with its own name
// to refer to it.

But what if we want it to act like a primitive object? What if we want a copy of wilmot to make changes to without affecting wilmot? Then we use a method of the object's class to create a new object that's a copy of that object. Usually this will be clone():
Hamster wilmot, fluffy;
wilmot=new Hamster();

Now fluffy is a copy of wilmot, and we can make changes to fluffy without affecting wilmot. Note: whether a class has the clone() method varies from class to class. Normally, if the class implements the Clonable interface then it has a clone() method that works this way.