equals/hashCode Under Inheritance — Interview Questions
Reviewed by Gurusankar M.
Short Answer
Adding a value field in a subclass makes it nearly impossible to keep equals() symmetric and transitive with its superclass — the classic Point/ColorPoint problem. You get two imperfect options: use getClass() (breaks Liskov — a subclass is never equal to its parent) or instanceof (can break symmetry/transitivity). The clean fixes: prefer composition over inheritance for value types, or define equality on a stable business key. With Hibernate, proxies and generated ids make identity-based equals dangerous.
On this page
Concept Made Simple
Coffee Chat Question
“How does the equals()/hashCode() contract break under inheritance, and with ORM proxies?”
Remember It Faster
Mind Map Answer
The trap: once a subclass adds a significant field, no equals() can satisfy reflexive + symmetric + transitive with the parent at the same time. It's a genuine design conflict, not a coding mistake.
- getClass()
- Symmetric, but breaks Liskov (no subtype equality)
- instanceof
- Allows subtype equality, risks asymmetry
- Best fix
- Composition over inheritance for value types
- Hibernate
- Use a business key, not the generated id
Hibernate/JPA: entities are wrapped in proxies (so getClass() != real class → use instanceof), and the id is null until persisted (so id-based hashCode changes after save, corrupting HashSets). Equality on an immutable natural/business key avoids both.
Key takeaway: value equality and inheritance don't mix cleanly — favour composition, base equality on immutable business keys, and never key a HashSet on a field that changes after construction/persistence.
Learn by Doing
Hands-on Keyboard
@Entity
class User {
@Id @GeneratedValue Long id; // null until persisted!
@Column(unique = true) String email; // stable business key
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User other)) return false;
return email != null && email.equals(other.email); // key, not id
}
@Override public int hashCode() {
return Objects.hashCode(email); // stable across persist
}
}Think Beyond the Expected
What If?
Why is basing a JPA entity's hashCode on its @GeneratedValue id dangerous?
Because the id is null before the entity is persisted and gets assigned on flush — so an entity's hashCode changes after you've already added it to a HashSet/HashMap. Once the hash changes, the collection can't find the element (it's in the wrong bucket), leading to duplicates, lost lookups, and leaks. Base equals/hashCode on an immutable business key (email, natural id) that's set at construction and never changes, so identity is stable across the entity's whole lifecycle.
Real World
Any project mixing JPA entities with HashSet/HashMap membership hits this: entities 'disappear' from sets after being saved, or bidirectional associations behave oddly, all because id-based hashCode changed on flush. Teams standardise on business-key equality (or documented reference equality) to keep collections correct.
Interviewer's Expectation
Keywords they're listening for:
- subclass value field breaks symmetry
- getClass breaks Liskov, instanceof risks asymmetry
- composition over inheritance
- business key over generated id
- Hibernate proxy + null-id pitfalls
Common Mistakes
- Basing entity hashCode on a generated id
- Mixing inheritance with value equality
- Using getClass() and being surprised proxies never match
Best Practices
- Prefer composition for value types
- Use immutable business keys for entity equality
- Consider records for simple immutable value objects
Follow-up Questions
- getClass() vs instanceof in equals — trade-offs?
- Why do records sidestep some of these issues?
- How do Hibernate proxies affect getClass() comparisons?
Continue Learning with AI
Take this question deeper with your favourite AI assistant. Pick a depth, copy the prompt, or open it directly — AI is your learning companion, not a shortcut.
Plain-language foundations
I'm preparing for a software engineering interview and want to understand this from scratch, as a beginner. Topic: Object Contract (Advanced Java) Interview question: "How does the equals()/hashCode() contract break under inheritance, and with ORM proxies?" Please: 1. Explain the core idea in simple, plain language, using an everyday analogy. 2. Define any technical terms you use. 3. Walk through one small, concrete example. 4. Finish with a single sentence I can easily remember. Keep the tone friendly and assume I'm new to this topic.
Was this answer helpful?
Affiliate links
Tools for Your Developer Workspace
Equipment for a comfortable, productive setup.
As an Amazon Associate I earn from qualifying purchases.
- MonitorView on Amazon : BenQ EW2790U 27-inch Monitor (opens in a new tab)
BenQ EW2790U 27-inch Monitor
- LaptopView on Amazon : Acer Aspire One Laptop (opens in a new tab)
Acer Aspire One Laptop
- Office ChairView on Amazon : Wakefit Gravita Ergonomic Office Chair (opens in a new tab)
Wakefit Gravita Ergonomic Office Chair