The Object Class
The default equals(obj) inherited from class Object tests whether two references point to the same object, which gives the same result as ==.
Two Assumptions Beginners Bring to Objects
Many programmers arriving at Java hold two working assumptions about objects. The first is that if two objects hold the same data, a test for equality will report that they match. The second is that printing an object will display its useful contents. Both assumptions feel reasonable, and both are wrong for the default behavior Java provides. This article takes each belief in turn, shows what Java actually does, and then shows how a class can change that behavior by overriding two methods it inherits from class Object.
What do you think happens?
Suppose you have a class called PairOfDice, and you create two separate PairOfDice objects, each showing a 3 and a 6. Each object stores its own pair of numbers. What do you think a call to equals() between the two objects will return?
Reveal answer
false
The default equals() inherited from Object tests whether two references point to the same object. These are two separate objects, so the default test returns false even though their stored numbers match.
What the Default equals() Actually Tests
The default equals(obj) inherited from class Object tests whether two references point to the same object. It gives the same result as the == operator. Its method signature is public boolean equals(Object obj). Because every class inherits this method from Object, every object has an equals() method available, even if the class never mentions it.
The two forms of comparison answer different questions. The expression == asks whether two references name the same object. The expression obj1.equals(obj2) asks the question that the class itself has chosen to answer. With the default implementation, the two forms agree. Once a class overrides equals() to compare contents, the two forms can disagree, and the result of obj1.equals(obj2) depends on what that particular class decided equality means.
The diagram shows the first case in the top group. Each variable refers to its own object, so the default equals() returns false, even though both objects store a 3 and a 6. In the bottom group, both variables refer to one object. The default equals() returns true, because the two references name the same object. The default test never looks at the numbers inside the objects at all.
Overriding equals() to Compare Contents
The source explains that it is common for a class to override equals() so that two of its objects count as equal when they have the same contents. The String class does this: two Strings are equal when they contain the same sequence of characters. The example below is an original illustration of the same idea for a small Card class. Its equals() method checks that the argument is a Card, then compares the fields that define a card.
public class Card {
private String suit;
private int rank;
public Card(String suit, int rank) {
this.suit = suit;
this.rank = rank;
}
public boolean equals(Object obj) {
if (!(obj instanceof Card)) {
return false;
}
Card other = (Card) obj;
return this.rank == other.rank && this.suit.equals(other.suit);
}
}- 1The class declares two instance variables that define a card.
- 2The constructor stores the values passed in.
- 3The equals method keeps the same parameter type as the inherited version, so Java uses this version when
equals()is called. - 4If the argument is not a Card, the method returns false immediately.
- 5Otherwise the method compares the rank values and the suit strings and returns true only when both match.
- The signature public boolean
equals(Object obj)matches the inherited method exactly. - The instanceof check rejects arguments that are not Cards.
- The cast gives access to the other card's fields.
- Two cards are equal when their rank and suit match, regardless of which objects they are.
Card a = new Card("Hearts", 7);
Card b = new Card("Hearts", 7);
System.out.println(a.equals(b));Class Card does not override equals(), so the inherited default compares references. The two cards are separate objects, so the result is false.
Card a = new Card("Hearts", 7);
Card b = new Card("Hearts", 7);
System.out.println(a.equals(b));Class Card overrides equals() to compare contents. The two cards have the same suit and rank, so the result is true.
Mistakes When Writing equals()
Common mistake Writing equals() with a parameter type other than Object
What it looks likepublic boolean equals(Card other) { ... }Why it failsThis is a different method from the inherited
equals(Object obj). Java still calls the default version when you compare objects, so your logic is silently ignored.Do this insteadUse the exact signature public boolean equals(Object obj), then cast the argument after checking its type.
Common mistake Assuming == and equals() always agree
Why it failsWith the default implementation they agree, but once a class overrides
equals()to compare contents,obj1.equals(obj2)can return true while obj1 == obj2 returns false.Do this insteadCheck whether the class overrides equals() before deciding how two objects will compare.
The Default toString() Output
The instance method toString() in class Object returns a value of type String that is supposed to represent the object as text. The default version returns the name of the class the object belongs to, joined with a code number called the object's hash code.
The source describes this default as not very useful. It tells you the kind of object you have, but not what the object contains. A printed line that shows only a class name and a number therefore tells a programmer that Java used the inherited method, and nothing about the values stored inside. When you create a class, you can write a new toString() method, which replaces the inherited version. The replacement is what Java will use from then on whenever it needs a string form of your objects.
Writing a toString() for PairOfDice
The source gives a toString() method for the PairOfDice class. It uses the two instance variables die1 and die2, which hold the numbers showing on the dice. When the two numbers are equal, the method reports a double. Otherwise it reports the two numbers separated by the word and.
public String toString() {
if (die1 == die2) {
return "double " + die1;
}
else {
return die1 + " and " + die2;
}
}- 1The method compares the two dice values.
- 2If they are equal, it builds a string starting with the word double followed by the value.
- 3Otherwise it joins the two values with the word and between them.
- 4The returned string describes what the object actually holds, which the default version never did.
- The method name and return type match the inherited
toString()in Object. - The string describes the state of the object rather than only its class.
3 and 6 (when die1 is 3 and die2 is 6)
double 2 (when die1 and die2 are both 2)Tracing toString() for Two Different Rolls
A PairOfDice object has die1 equal to 5 and die2 equal to 1. Then a second object has die1 equal to 2 and die2 equal to 2. What string does toString() return for each?
- 1Check the first object
die1 is 5 and die2 is 1. The values are not equal, so the else branch runs.
- 2Build the first string
The method returns die1 + " and " + die2, which is 5 and 1.
- 3Check the second object
die1 is 2 and die2 is 2. The values are equal, so the if branch runs.
- 4Build the second string
The method returns "double " + die1, which is double 2.
The first object gives 5 and 1, and the second gives double 2.
When Java Calls toString() Automatically
You have probably already used toString() without calling it yourself. When you use an object in a context that requires a string, Java converts the object to type String by calling its toString() method. Printing an object and concatenating an object onto a string are the two common cases. The conversion is automatic, which is why a missing toString() override tends to show up in printed output rather than as an error. If you print an object and see a class name followed by a code number, Java has used the inherited default version, and that is a signal to check whether the class defines its own toString().
The sequence above follows the source's example of a PairOfDice object inside a print statement. The program asks for the object to be printed. Because printing needs text, Java calls the object's toString() method and uses the string it returns. Concatenation follows the same pattern: when an object is joined onto a string with the plus operator, Java first obtains its string form from toString().
Object at the Root of Every Class Chain
Most of what you have learned about object-oriented programming so far centers on subclasses. A subclass takes on the properties and behaviors of an existing class, and it can change those inherited behaviors or add new ones of its own. This section looks at a side of the same system you may not have noticed yet: every class in Java, with one exception, already sits underneath some other class. The class that makes this possible is named Object. Object is a special class in package java.lang and is the one class in Java that is not a subclass of any other class.
Think of Object as the root of the whole class structure. Every chain of parents, followed upward from any class you write, eventually reaches Object. The chain can be one step long, as with a class that has no explicit parent, or several steps long, as with a class that inherits from a class that itself inherits from another. In every case, the top of the chain is Object.
The diagram shows a small hierarchy. Animal has no extends clause, so it sits directly under Object. Dog is written to extend Animal, so it sits one level below Animal. Car has no extends clause either, so it sits directly under Object as well. Follow any arrow upward and you reach Object. The source does not walk through this exact example, but it follows directly from its statements that every class except Object is a subclass of some other class and that a class with no explicit parent becomes a subclass of Object.
Finding the Superclass of a Class
Determining the Superclass of Three Classes
Classes Animal, Dog, and Car are declared as follows: Animal has no extends clause, Dog extends Animal, and Car has no extends clause. What is the superclass of each class?
- 1Check Animal
Animal has no extends clause, so it automatically becomes a subclass of Object. Its superclass is Object.
- 2Check Dog
Dog states extends Animal, so its direct superclass is Animal. Animal's superclass is Object, so Dog's chain is Dog, Animal, Object.
- 3Check Car
Car has no extends clause, so its superclass is Object, the same as Animal.
Animal and Car have Object as their superclass. Dog has Animal as its direct superclass, and every chain ends at Object.
- Animal has no extends clause, so its superclass is Object.
- Dog extends Animal, so its superclass is Animal.
- Car has no extends clause, so its superclass is Object.
Why Every Object Gets Object's Methods
Because every class is a subclass of Object, a variable whose type is Object can refer to an object of any class you write. The second major fact in this section concerns behavior rather than structure. Class Object defines several instance methods, and because every other class is a subclass of Object, every other class inherits those methods. The source says these methods can be used with any object whatsoever. You do not need to write any special code in a class for its objects to have access to the methods defined in Object.
- Each variable has type Object but refers to a different class's object.
equals()andtoString()are called on each object without any code in Dog, Car, or PairOfDice declaring them directly.- Each call uses whichever version the object's class has provided, either its own override or the inherited default.
Tracing equals() and toString() Through Code
A class Card overrides equals() to compare suit and rank, as shown earlier. Two variables, x and y, are declared as new Card("Spades", 4) and new Card("Spades", 4). A third variable, z, refers to the same object as x. Predict the result of x.equals(y), x == y, and x.equals(z). Then say whether printing x would show the card's contents if Card does not override toString().
Hints
- x and y are separate objects, so == compares references and returns false.
- The overridden
equals()compares rank and suit, sox.equals(y)returns true. - x and z refer to the same object, so x == z is true, and
equals()also returns true. - Without an override, printing x shows only the class name and hash code, not the suit or rank.
Key Takeaways
- The default
equals(obj)inherited from class Object tests whether two references point to the same object, so it gives the same result as ==. Two separate objects with identical contents are not equal under the default. - A class overrides
equals()using the exact signature public booleanequals(Object obj)to define equality by contents, as the String class does for character sequences. - The default
toString()returns the class name and hash code, which rarely describes the object's state, so classes usually write their owntoString()method. - Java calls
toString()automatically when an object is used in a string context, such as printing or concatenation. - Object is the one class that is not a subclass of any other class. Every class without an explicit parent becomes a subclass of Object, so every superclass chain ends at Object, and every object can use Object's methods.