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)