
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car car1 = new Car();
car1.model = "Ferrari";
car1.maxSpeed = 300;
Car car2 = new Car();
car2.model = "Ferrari";
car2.maxSpeed = 300;
System.out.println(car1 == car2);
}
}
Console output:
false
It seems we've created two identical Car objects: the values of the corresponding fields of the two car objects are the same, but the result of the comparison is still false.
We already know the reason: the car1 and car2 references point to different memory addresses, so they are not equal.
But we still want to compare the two objects, not two references. The best solution for comparing objects is the equals() method.
equals() method
You may recall that we don't create this method from scratch, rather we override it: the equals() method is defined in the Object class. That said, in its usual form, it is of little use:
public boolean equals(Object obj) {
return (this == obj);
}
This is how the equals() method is defined in the Object class. This is a comparison of references once again. Why did they make it like that?
Well, how do the language's creators know which objects in your program are considered equal and which are not? :)
This is the main point of the equals() method — the creator of a class is the one who determines which characteristics are used when checking the equality of objects of the class. Then you override the equals() method in your class.
If you don't quite understand the meaning of "determines which characteristics", let's consider an example.
Here's a simple class representing a man: Man
.
public class Man {
private String noseSize;
private String eyesColor;
private String haircut;
private boolean scars;
private int dnaCode;
public Man(String noseSize, String eyesColor, String haircut, boolean scars, int dnaCode) {
this.noseSize = noseSize;
this.eyesColor = eyesColor;
this.haircut = haircut;
this.scars = scars;
this.dnaCode = dnaCode;
}
// Getters, setters, etc.
}
Suppose we're writing a program that needs to determine whether two people are identical twins or simply lookalikes.
We have five characteristics: nose size, eye color, hair style, the presence of scars, and DNA test results (for simplicity, we represent this as an integer code).
Which of these characteristics do you think would allow our program to identify identical twins?

equals()
method?
We need to override it in the Man
class, taking into account the our program's requirements.
The method should compare the int dnaCode
field of the two objects. If they are equal, then the objects are equal.
@Override
public boolean equals(Object o) {
Man man = (Man) o;
return dnaCode == man.dnaCode;
}
Is it really that simple? Not really. We overlooked something.
For our objects, we identified only one field that is relevant to establishing object equality: dnaCode
.
Now imagine that we have not 1, but 50 relevant fields. And if all 50 fields of two objects are equal, then the objects are equal.
Such a scenario is also possible. The main problem is that establishing equality by comparing 50 fields is a time-consuming and resource-intensive process.
Now imagine that in addition to our Man
class, we have a Woman
class with exactly the same fields that exist in Man
.
If another programmer uses our classes, he or she could easily write code like this:
public static void main(String[] args) {
Man man = new Man(........); // A bunch of parameters in the constructor
Woman woman = new Woman(.........); // The same bunch of parameters.
System.out.println(man.equals(woman));
}
In this case, checking the field values is pointless: we can readily see that we have objects of two different classes, so there is no way they can be equal!
This means we should add a check to the equals()
method, comparing the classes of the compared objects. It's good that we thought of that!
@Override
public boolean equals(Object o) {
if (getClass() != o.getClass()) return false;
Man man = (Man) o;
return dnaCode == man.dnaCode;
}
But maybe we've forgotten something else?
Hmm... At a minimum, we should check that we are not comparing an object with itself! If references A and B point to the same memory address, then they are the same object, and we don't need to waste time and compare 50 fields.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (getClass() != o.getClass()) return false;
Man man = (Man) o;
return dnaCode == man.dnaCode;
}
It also doesn't hurt to add a check for null
: no object can be equal to null
. So, if the method parameter is null, then there is no point in additional checks.
With all of this in mind, then our equals()
method for the Man
class looks like this:
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Man man = (Man) o;
return dnaCode == man.dnaCode;
}
We perform all the initial checks mentioned above. At the end of day, if:
- we are comparing two objects of the same class
- and the compared objects are not the same object
- and the passed object is not
null
dnaCode
fields of the two objects. When overriding the equals()
method, be sure to observe these requirements:
Reflexivity.
When the
equals()
method is used to to compare any object with itself, it must return true.
We've already complied with this requirement. Our method includes:if (this == o) return true;
Symmetry.
If
a.equals(b) == true
, thenb.equals(a)
must returntrue
.
Our method satisfies this requirement as well.Transitivity.
If two objects are equal to some third object, then they must be equal to each other.
Ifa.equals(b) == true
anda.equals(c) == true
, thenb.equals(c)
must also return true.Persistence.
The result of
equals()
must change only when the fields involved are changed. If the data of the two objects does not change, then the result ofequals()
must always be the same.Inequality with
null
.For any object,
a.equals(null)
must return false
This is not just a set of some "useful recommendations", but rather a strict contract, set out in the Oracle documentation
GO TO FULL VERSION