IS-A vs HAS-A Relationship in Java
Fast recall
Quick revision
Core answer
Dog IS-A Animal), implemented with extends/implements. HAS-A is composition: an object contains another and uses it (Car HAS-A Engine), implemented by holding a field and delegating. Run the IS-A test first; if it fails, model it as HAS-A.Whether you can apply the IS-A vs HAS-A test to choose between inheritance and composition.
Forcing an IS-A (inheritance) relationship where HAS-A (composition) is correct, just to reuse code.
Note composition (HAS-A) is usually the safer default and that 'favor composition over inheritance' follows directly from this test.
Interview-ready guide
Interview answer
These two are really a decision test for inheritance versus composition. IS-A is inheritance — a dog is a kind of animal, a circle is a kind of shape — expressed with extends or implements, so the child can stand in anywhere the parent is expected. HAS-A is composition — a car isn't a kind of engine, it owns one — expressed by holding the other object as a field and delegating calls to it.
The way I decide is to say the sentence out loud. 'A car is a kind of engine' sounds wrong instantly, which tells me composition. People get into trouble forcing inheritance just to reuse code — Car extends Engine drags in the engine's whole API, exposes internals that make no sense on a car, and locks you into a hierarchy you can't easily change.
So my default is composition. It's loosely coupled — swap the Engine field for an electric motor and nothing using Car notices — whereas inheritance is rigid once set. That's exactly why the Gang of Four say favour composition over inheritance: not that inheritance is wrong, but that you reach for IS-A only when the type relationship genuinely holds.
IS-A vs HAS-A Relationship in Java
Direct Answer
IS-A is inheritance: a subtype is a kind of its supertype (Dog IS-A Animal), implemented with extends/implements. HAS-A is composition: an object contains another and uses it (Car HAS-A Engine), implemented by holding a field and delegating. Run the IS-A test first; if it fails, model it as HAS-A.
Common Mistakes & Pitfalls
- Forcing IS-A just to reuse code — like
class Car extends Engine; you inherit the parent's entire public API, including methods that make no sense on the child, and leak its internals to every caller. - Locking yourself into a rigid hierarchy — inheritance is fixed at compile time, so swapping the engine for an electric motor means reworking the class tree instead of changing one field.
- Skipping the sentence test — if you never say 'a car is a kind of engine' out loud, you miss the obvious signal that composition was the right model.
- Treating 'favour composition over inheritance' as 'never inherit' — inheritance is correct when IS-A genuinely holds; the advice is about defaulting to HAS-A, not banning
extends.
Check your understanding
Answer these without looking back at the article.
- Why do design patterns favor composition over inheritance?
- Can you give an example where using inheritance was the wrong choice?
- How does dependency injection relate to the HAS-A relationship?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.