1. Follow the equals()
Contract
A correct equals()
method must satisfy the following contract rules:
✅ Reflexivity
An object must be equal to itself.
Person p = new Person("Alice", 25);
assert p.equals(p); // true
✅ Symmetry
If a.equals(b)
, then b.equals(a)
must also be true
.
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
assert p1.equals(p2) == p2.equals(p1); // true
✅ Transitivity
If a.equals(b)
and b.equals(c)
, then a.equals(c)
must be true
.
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
Person p3 = new Person("Alice", 25);
assert p1.equals(p2) && p2.equals(p3) && p1.equals(p3); // true
✅ Consistency
Multiple calls to equals()
must return the same result unless the object’s state changes.
Person p1 = new Person("Alice", 25);
Person p2 = new Person("Alice", 25);
assert p1.equals(p2); // true
assert p1.equals(p2); // true (even if called multiple times)
✅ Null Handling
equals(null)
must always return false
and should not throw an exception.
Person p = new Person("Alice", 25);
assert !p.equals(null); // false
2. Use the @Override
Annotation
Always mark the method with @Override
to ensure you are correctly overriding equals()
from Object
.
@Override
public boolean equals(Object obj) { ... }
3. Check for Reference Equality First (this == obj
)
Before performing field comparisons, check if the references are the same. This is an optimization that improves performance.
if (this == obj) return true; // Same memory reference
4. Check for null
and Ensure Type Matching
- If
obj
isnull
, returnfalse
immediately. - Ensure the class types match to avoid
ClassCastException
.
if (obj == null || getClass() != obj.getClass()) return false;
🔹 Alternative:
Some implementations allow instanceof
instead of getClass()
, but this can break symmetry in inheritance.
5. Cast and Compare Significant Fields
After checking type compatibility, cast obj
to the correct type and compare important fields.
Person person = (Person) obj;
return this.age == person.age && Objects.equals(this.name, person.name);
🔹 Use Objects.equals()
instead of ==
for object fields to avoid NullPointerException
:
return Objects.equals(this.name, person.name);
6. Ensure equals()
is Consistent with hashCode()
- If two objects are equal (
equals()
returnstrue
), they must have the samehashCode()
. - Otherwise, hash-based collections (like
HashSet
andHashMap
) may not work correctly.
@Override
public int hashCode() {
return Objects.hash(name, age);
}
Example: Correctly Overriding equals()
import java.util.Objects;
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object obj) {
if (this == obj) return true; // Rule 3: Self-check
if (obj == null || getClass() != obj.getClass()) return false; // Rule 4: Type check
Person person = (Person) obj;
return age == person.age && Objects.equals(name, person.name); // Rule 5: Compare fields
}
@Override
public int hashCode() {
return Objects.hash(name, age); // Rule 6: Keep consistent with equals()
}
}
Common Mistakes to Avoid
🚫 Forgetting to override hashCode()
when overriding equals()
✅ Always override hashCode()
to maintain consistency.
🚫 Using ==
to compare object fields
✅ Use .equals()
or Objects.equals()
for objects.
🚫 Allowing equals()
to throw exceptions
✅ equals(null)
should always return false
, not throw an exception.
🚫 Using instanceof
instead of getClass()
in some cases
✅ instanceof
may break symmetry in inheritance.
Final Summary
✅ Follow the equivalence relation rules (Reflexivity, Symmetry, Transitivity, Consistency, Null Handling).
✅ Optimize performance by checking this == obj
first.
✅ Ensure type safety using getClass()
or instanceof
carefully.
✅ Use Objects.equals()
to avoid NullPointerException
.
✅ Ensure hashCode()
is overridden to match equals()
.
By following these rules, your overridden equals()
method will be correct, efficient, and compatible with Java collections. 🚀