Inheritance allows one class (called the subclass or child class) to inherit the properties and behaviors of another class (called the superclass or parent class).
In simpler terms:
Inheritance enables code reuse by letting you define common logic once in a base class and then extend or specialize it in multiple derived classes.
This leads to cleaner, modular, and more maintainable software.
Think of a User system in a web application:
User class holds common attributes like username, email, and methods like login() or logout().Admin, Customer, and Vendor inherit from User but add role-specific behavior.All specialized user types inherit common data and behaviors from the User class, but can extend functionality to suit their roles.
Inheritance offers several benefits that make it a powerful design tool in OOP.
It embodies the DRY (Don't Repeat Yourself) principle. Common logic is written once in the parent class and shared across all subclasses reducing redundancy.
It creates a clear and intuitive hierarchy that model real-world “is-a” relationships like ElectricCar is a Car or Admin is a User.
If a bug is found or a change is needed in the shared logic, you only need to fix it in one place, the superclass. All subclasses automatically inherit the fix.
Inheritance is a prerequisite for polymorphism, allowing objects of different subclasses to be treated as objects of the superclass.
When a class inherits from another:
This allows for both reuse and customization.
Let’s model a simple vehicle system.
This Car class defines basic attributes and common behaviors shared by all cars.
Now you can create specialized types of cars:
In this example:
ElectricCar and GasCar inherit the make, model, startEngine(), and stopEngine() methods from the Car class.Inheritance is powerful, but it should be used intentionally, only when it truly models a real-world relationship.
Dog is an Animal, Car is a Vehicle)Car has an Engine, not is an Engine)In these cases, composition is usually a better choice.
While inheritance is powerful, it's often overused. Incorrectly applying it can lead to rigid, fragile designs that are hard to maintain.
This is why many modern OOP designs favor composition over inheritance for better modularity and flexibility.
Both Inheritance and Composition define relationships between classes but they serve different purposes and offer different trade-offs.
Aspect | Inheritance | Composition |
|---|---|---|
Relationship | “is-a” | “has-a” or “uses-a” |
Coupling | Tightly coupled | Loosely coupled |
Flexibility | Compile-time (fixed) | Runtime (dynamic) |
Best for | Shared logic & hierarchy | Reusable and pluggable components |
Example |
|
|
Prefer composition over inheritance when:
Instead of this:
Do this:
Here, the Printer has a Logger, not is a Logger. This keeps the design modular, testable, and loosely coupled.
Inheritance lets child classes share and extend behavior but what if multiple subclasses need to behave differently when responding to the same method call?
That’s where Polymorphism comes in.
In the next chapter, we’ll explore how polymorphism enables a single interface to represent multiple implementations allowing your code to be flexible, elegant, and truly object-oriented.