Comparing Objects

Things To Do Later

Working with Objects

Figure A: an empty object variable

We've said it often - variables that hold objects are not like variables that hold primitive data. Variables that hold objects are object variables or reference variables. The word "reference" is key here. Examine Figure A above: This image shows what the object variable "roomOne" looks like in memory after the statement:

RoomType roomOne;

This statement declares a variable called roomOne that will hold an object based on the RoomType class (an instance of RoomType). Because no object has yet been constructed in this statement, the roomOne object variable contains the value "null". The null value is like a 0 for objects - it means that the variable doesn't have an object yet

Figure B: an object variable referencing an object

Figure B shows what the roomOne variable would look like in memory if you had assigned it a newly constructed Room object instead. The new operator is used to construct new objects. It's referring to the Room() constructor, which is receiving two arguments that will be assigned to the length and width instance variables of the room object being created. The address of the newly constructed room object is then stored in the roomOne variable. The address is a reference to the object in memory, which is why object variables are also called reference variables.

Object variables don't contain objects; rather, they refer to objects in memory. For example, the roomOne variable contains a reference to a Room object in memory. When the object variable contains the null value, as in Figure A, it means that it is not referencing any object in memory.

Figure B shows what happens in memory once the RoomType class is instantiated. This figure represents memory after the statements:

RoomType roomOne = new RoomType(20, 25);

You could also type this as:

RoomType roomOne;
roomOne = new RoomType(20, 25);

In Figure B, the variable roomOne is declared and given a new instance of the Room class. In memory, as you can see by Figure B, the RoomType object is instantiated in memory, and its two private instance variables are given the values 20 and 25.

The roomOne variable contains a reference (memory address) to the object in memory. So you can see that the object variable does not actually contain the object; it contains the memory address of the object! This becomes important when you begin working with multiple objects in a program, as we'll see in the next example.

Figure C: 2 object variables, each referencing its own object

When you create two object variables and assign a different object to each of them, memory will look much like the image in Figure C. In this example, there are 2 reference variables, and each variable is pointing to its own object.

It is possible to have two reference variables pointing to the same object in memory! Try the following program using your Circle class:

public class TryObjectRef {

    public static void main(String[] args) {

        Circle c1 = new Circle(5);
        System.out.println(c1);

        Circle c2 = new Circle(8);
        System.out.println(c2);
    }
}

Line 5 defines a new Circle object for the object variable "c1". A circle has a radius, so we give this circle object a radius of 5.

Line 6 prints the c1 object variable - it's referencing the Circle object, so it will go find that object and print its information. Recall that if a class contains a toString() method, the println() will search for that method, grab the String returned by the method, and use it as the argument to the println() method.

Lines 8 and 9 are similar to 5 and 6 - a new Circle object is created with radius 8. Then this circle's information is displayed on the console.

If you compile and run this program, you should see:

Circle: radius = 5.0
Circle: radius = 8.0

Now go back to your program and add a new line between line 8 and 9:

c2 = c1;

Re-compile your program and run it. You'll now see:

Circle: radius = 5.0
Circle: radius = 5.0
Figure D: both c1 and c2 are referencing the same circle object

What is happening in the statement c2 = c1;? Recall that object variables contain the memory addresses to the objects in memory. This statement takes the variable c2 and gives it the value in c1. The c1 variable contains the address of the first circle object, so c2 now also gets the address of the first circle object in memory.

The c2 variable was pointing to the second circle object, but then we assigned the value of c1 to c2. This made c2 receive the address of the first circle, so now c2 is pointing to the first circle. Since the second circle has no variable referencing it, Java will eventually delete it from memory.

The equals() Method

Given what we now know about objects in memory, what do you think occurs when the following program runs?

public class ObjectComp {
	
    public static void main(String[] args) {

        Circle c1 = new Circle(5);
        Circle c2 = new Circle(5);
		
        System.out.println(c1 == c2);
    }
}

What do you think will appear on your screen when the program is run? What actually does appear?

The == operator compares to see if the two operands on either side of the operator are of equal value. If we were to compare two integers, such as 5 == 2, we would know that this statement would result in the boolean value false. Similarly, we know that the statement 5 == 5 would result in the boolean value true. When comparing two variables, we know that Java will look at the values inside those variables and compare them. When Java looks inside the variables c1 and c2, it sees memory addresses. It then compares these two memory addresses -- is the memory address in c1 equal to the memory address in c2? In this case, c1 and c2 are referencing two completely different objects in memory, so the memory addresses in the variables are not the same. This is why you see the boolean value false display on the console.

What if you wrote a class and wanted a programmer to be able to compare two objects for equality? For example, we know that the circle object in c1 is equivalent to the circle object in c2, because they have the same radius. We could assume the programmer would just compare the radius of the two circle objects. What if we were working with employee objects; what would make one employee object "equal" to another employee object?

Programmers will often think of this when designing their classes, and so they will often include a method that allows another programmer to compare to objects for equality. The method we generally use is the equals() method. You've used the equals() method before to compare strings (remember, Strings are objects!) so now we'll add our own equals() method to our classes.

The equals() method is part of Java, like the toString() method. the equals() method is public and returns a boolean value of true if two objects are equal, and false if two objects are not equal. The equals method will also require a parameter variable to hold the second object you are comparing, and this parameter should always be of the type Object.

What?! What is the type Object? There is indeed a class called Object! In Java, the class Object is the parent of all classes, even the classes you create. Your classes inherit the public members of the Object class, and that includes the toString() and the equals() methods. When you're getting that weird, default toString() value we mentioned earlier, that's where it comes from: the Object class.

When you add an equals() method to your own classes, you need to make sure the parameter type is Object. Why? This is an advanced topic that you'll learn more about in Java 2.

So let's say you are writing an equals() method for your Employee class, your equals method signature would be:

public boolean equals(Object emp)

You would call this method by invoking it on one employee object, and passing it the other employee object:

boolean sameGuys = someEmployee.equals(someOtherEmployee)

Similarly, the signature for an equals() method in a Robot class would be:

public boolean equals(Object robot)

You would then invoke this with a statement such as:

if (bob.equals(robby)) {
    // ....
}

Note that this works the same if you are comparing strings. For example, to see if the string variable login is equal to the string value "admin", you would say:

if (login.equals("admin")) {

The code in the equals() method will vary from class to class. As a programmer, you would have to determine what makes two objects equal. For example, what would make two circle objects equal? You could probably test the two radius values:

public boolean equals(Object c) {
	return this.radius == c.getRadius();
}

The problem here, is that right now the parameter "c" is of type Object, not of type Circle. If you compile this code, you'll get an error that Java can't find the getRadius() method in the class Object. c is an Object right now, it's not exactly a Circle (yes, deep down inside it's a Circle, but on the surface, it's an Object).

In order to use methods that belong to Circle, you have to cast your Object parameter c into a Circle first:

public boolean equals(Object c) {
    Circle circle = (Circle)c;
    return this.radius == circle.getRadius();
}

This equals() method will first cast the Object c back into a Circle object, then it will check the current object's radius (the circle on which this method is being invoked, referred to by this.radius) and the parameter variable's radius (referred to by circle.getRadius()) If the two are equal, the return statement will return a true value, otherwise it will return a false value.

Exercise

[solutions]

1. What would make two room objects equal? Write an equals() method for the Room class and test it out.

2. What would make two Time objects equal? Write the equals() method for the Time class.