Inheritance looks efficient in the first five minutes of a machine-coding round and expensive for the next fifty. The moment a subclass inherits a method it should not have, you are maintaining a lie about the type.
Consider a naive vehicle hierarchy:
class Vehicle {
void startEngine() { /* ... */ }
void refuel(int litres) { /* ... */ }
}
class Car extends Vehicle { }
class Bicycle extends Vehicle {
// A bicycle has no engine and no fuel tank, yet it inherits both.
// startEngine() and refuel() are now nonsense operations on a Bicycle.
}
The hierarchy models "is-a" by name but not by behavior. Every caller that holds a
Vehicle reference can call refuel on a Bicycle, and you are forced to either
throw at runtime or silently no-op. Both are design smells the interviewer will notice.
The root cause: inheritance couples you to everything the base class does, not just
the parts that actually apply. When requirements shift — an electric car, a cargo bike —
the base class becomes a battleground of if checks and overrides.