The ==
operator is a false friend in Java. It works properly only on primitive and will make you cry if you used it on any other referenced Object
, for the most part.
The basic
As a rule of thumb, every equals
method should start like this:
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instance ClassName)) {
return false;
}
// custom logic here
}
The ClassName should be replaced by whatever type your class is. After that, I usually apply one of the following solutions.
The class as only one relevant field
In the case that the class has only one relevant field, I will return automatically either the ==
operation (for primitive) or the equals
of the field.
public class Person
private String fullname;
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instance Person)) {
return false;
}
Person converted = (Person)obj;
return this.fullname.equals(converted.fullname);
}
}
The class as many relevant fields
I usually have two options here.
The toString()
contains all the relevant field
There is no need to add complexity in a class if something can help you with your code. There is no need to test for instanceof
since toString
is defined everywhere, and no need to cast for the same reason.
public class Person
private String firstname;
private String lastname;
@Override
public String toString() {
return this.firstname + " " + this.lastname;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
return this.toString().equals(obj.toString());
}
}
It is not mandatory to be the toString
. Sometimes other methods do the same job. You need to keep in mind that other methods will require to test for and cast the object before comparing.
There is no method to help
In that situation, I will usually cascade if
until the last relevant field.
public class Person
private String firstname;
private String lastname;
private Date dob;
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!(obj instance Person)) {
return false;
}
Person converted = (Person)obj;
if (!(this.firstname.equals(converted.firstname)) {
return false;
}
if (!(this.lastname.equals(converted.lastname)) {
return false;
}
return this.dob.equals(converted.dob);
}
}
I know that I could probably do something like this:
return this.firstname.equals(converted.firstname) && this.lastname.equals(converted.lastname) && this.dob.equals(converted.dob);
I don't really like this because it is mainly hard to read. I know that Java will probably optimize the bytecode with these kinds of statement, nevertheless, I believe that in most cases, it won't really matter.
Don't forget that, to be able to use HashMap
and stuff, you also need to override the hashcode
. Here are the main rules:
- If 2 instances are equal, then both
hashcode
MUST BE equal; - If 2 instances are not equal, then both
hashcode
SHOULD NOT BE equal.
A good hashcode
algorithm that lives up to the second rule will make the use of HashMap
and HashSet
more efficient.
Top comments (1)
Really nice and simple, I really like how you presented your chain of thought. Thanks for this :)