Inheritance lets a child class reuse and extend a parent class (an 'is-a' relationship: a Dog is an Animal). Polymorphism lets the same method call behave differently depending on the actual object. Together they enable code reuse and flexible designs — and they generate a cluster of favourite interview questions.
Overloading vs overriding
Overloading (compile-time/static polymorphism): same method name, different parameter lists, in the same class. Overriding (run-time/dynamic polymorphism): a child class redefines a parent's method with the same signature, and the call resolves to the object's actual type at run time.
Overriding gives run-time polymorphism
class Animal { String sound() { return "..."; } }
class Dog extends Animal { String sound() { return "Woof"; } }
class Cat extends Animal { String sound() { return "Meow"; } }
Animal a = new Dog();
a.sound(); // "Woof" — resolved by the actual object at run time⚡ The edge
- The classic trap: overloading is compile-time (same class, different parameters); overriding is run-time (subclass, same signature). Memorise that one sentence.
- Prefer composition over inheritance when you only need to reuse behaviour — deep inheritance hierarchies become rigid and fragile. Inheritance models 'is-a'; composition models 'has-a'.
Worked example
'What is the difference between overloading and overriding?'
- Overloading: multiple methods with the same name but different parameter lists in one class, resolved at compile time.
- Overriding: a subclass provides a new implementation of a parent method with the same signature, resolved at run time.
- So overloading is about the same class and parameters; overriding is about inheritance and dynamic dispatch.
Answer: Overloading = same name, different params, compile-time; overriding = subclass redefines, run-time.
Worked example
'Why prefer composition over inheritance?'
- Inheritance tightly couples a child to its parent; changes in the parent can ripple and break children.
- Deep hierarchies are rigid and hard to change, and a class can usually inherit from only one parent.
- Composition (holding an object and delegating to it) is more flexible and reusable — you assemble behaviour rather than locking into a hierarchy.
Answer: Composition is more flexible and loosely coupled; deep inheritance is rigid and fragile.
⚠ Watch out
- Overloading ≠ overriding — different parameters in one class vs same signature across parent/child.
- Multiple inheritance of classes causes the diamond problem; many languages forbid it and use interfaces instead.
- Inheritance is 'is-a'; if the relationship is really 'has-a', use composition.