This learning is something I personally encountered in a SonarQube rule violation fix PR raised by Steven. It led me to revisit how we have historically written (often IDE-generated) the equals() method in Java—and why things are shifting now.
🛠️ The Old Way: instanceof
For years, IntelliJ and other dev tools used the instanceof check inside equals():
if (!(obj instanceof MyClass)) {
return false;
}
This seemed fine—but there’s a hidden trap. It allows equality between subclass and superclass, which can violate the symmetry contract of equals():
a.equals(b)should return the same result asb.equals(a).
However, subclasses may have additional fields that the superclass doesn't compare—leading to inconsistent or buggy behavior in collections like HashSet, HashMap, or even in sorting and caching.
🚀 The Modern Approach: getClass()
IntelliJ and SonarQube now recommend:
if (obj == null || getClass() != obj.getClass()) {
return false;
}
This enforces strict class equality—avoiding the risk of broken symmetry and ensuring predictable behavior across use cases.
🧠 Why Does This Matter?
The equals() contract (reflexive, symmetric, transitive) has existed since Java 1.0.
But tools started enforcing this best practice in recent years due to:
- Bugs discovered in real-world code.
- New Java features like records and sealed classes.
✅ Key Takeaway
If your class is not designed for subclass comparison, prefer using getClass() in equals()—it’s safer and more predictable.
Top comments (0)