Subsection 5.7.3 Overriding the equals
Method
The
equals
method defined in
Object
and thus inherited by all classes only considers two object references equivalent if they refer to exactly the same object. But we saw in Unit 2 that the
String
class provides an
equals
method that considers two
String
objects equivalent if they have the same characters in the same order, even if they are actually different objects. How does that work?
It is because the
String
class has
overridden the
equals
method it inherited from
Object
to provide a definition of equality that makes more sense.
As we saw in section 9.3 a class can override inherited methods by providing a method with the same method signature (method name, parameter types, and return type).
String
has done that with
equals
so when we compare
String
objects with
equals
that new method will be called instead of the inherited one.
Activity 5.7.3.
Try to guess what this code will print out before running it.
However, overriding
equals
is a bit more involved than overriding
toString
. While the
toString
method is only required to produce a reasonable human-readable
String
representation of an object,
equals
has to meet a more complex set of requirements in order to be useful.
You will not be expected to write your own
equals
method on the AP exam but it’s worth looking at what those requirements are and how to satisify them. There are five requirements described in the Javadocs for
equals
in
Object
that a properly implemented
equals
must satisfy:
-
Equality is
reflexive, meaning an object will be
equals
to itself:
o.equals(o)
is
true
.
-
Equality is
symmetric:
o1.equals(o2)
returns the same value as
o2.equals(o1)
.
-
Equality is
transitive: if
o1.equals(o2)
and
o2.equals(o3)
then
o1.equals(o3)
.
-
Equality is
consistent:
o1.equals(o2)
always returns the same value assuming the objects are not modified.
-
No object is equal to
null
:
o.equals(null)
is always
false
.
The other way to look at these requirements is as guarantees that are made to you as a user of
equals
. If you look at it that way, these requirements are quite nice. Imagine how much harder it would be to use the
String
equals method if you couldn’t rely on the fact that
s1.equals(s2)
is necessarily the same as
s2.equals(s1)
!
So even though the Java compiler can’t force you to implement
equals
correctly, if you ever do want to override it, it’s important that you do. Let’s look at what’s involved.
We’ll write a class
Word
which represents a word in a particular language. We want two
Word
objects to be considered
equals
if and only if they are spelled the same and come from the same language. The latter requirement is because sometimes different languages have words that are spelled the same but with different meanings such as “pie” which in English is a tasty baked treat and in Spanish is what we call a “foot” in English.
Activity 5.7.4.
Try to guess what this code will print out before running it. Click on the CodeLens button to step forward through the code and watch the memory.
The basic recipe for writing your own equals method, is:
-
Use the
public boolean equals(Object other)
method signature. Make sure the parameter type is
Object
, not the class you are defining.
-
Check of
this == other
to quickly return
true
when comparing an object to itself.
-
Use
instanceof
to check if other is an instance of this class and return
false
if not.
-
Cast
other
to the current class.
-
Finally compare this object’s attributes to the other object’s with
==
for primitive types like
int
and
double
and
equals
for reference types. If you need to compare multiple attributes
&&
together the comparisons of the individual attributes since two objects should only be equal if all the attributes match.
Note that the requirements on
equals
make it almost impossible to correctly override it in a subclass of a class that has already overridden the
Object
version. To see why, imagine if we made a subclass of
Word
,
ClassifiedWord
and added another attribute,
partOfSpeech
.
If we override
equals
in the
ClassifiedWord
to only consider two
ClassifiedWord
objects
equals
if their spelling, language, and part of speech match, that will break the symmetry since
regularWord.equals(classifiedWord)
will invoke the
equals
from
Word
which will only compare the spelling and language of the word but
classifiedWord.equals(regularWord)
will return
false
assuming the
equals
in
ClassifiedWord
checks that
other
is an
instanceof ClassifiedWord
. In general you should only provide an overridden
equals
method in one class in a class hierarchy.