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
objisnull, returnfalseimmediately. - 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
HashSetandHashMap) 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. 🚀