OOP Fundamentals
Inheritance
Hierarchical code reuse
Inheritance is the OOP mechanism by which one class (the subclass or derived class) acquires the state variables and behaviors of another class (the superclass or base class). It establishes an "Is-A" relationship, facilitating structured code reuse.
1. Learning Objectives
- Establish hierarchical relationships using base and derived classes.
- Understand constructor chaining and class execution order.
- Differentiate between the structural implications of Inheritance vs Composition.
2. Problem Statement
Without inheritance, modeling different types of objects in the same domain leads to massive duplication. If you build an Employee Payroll System, you would copy-paste fields like id, name, and salary across FullTimeEmployee, PartTimeEmployee, and Contractor classes, multiplying technical debt.
3. Real-world Analogy
Think of a classification hierarchy of vehicles.
A Vehicle is the base concept (has speed, carries cargo). A Car is a subclass of Vehicle: it inherits speed controls but adds car-specific details (four passenger seats, windshield wipers). A SportsCar is a subclass of Car: it inherits passenger capacities but adds sports performance details (turbochargers, carbon fiber spoilers).
4. Theory
Inheritance defines how properties cascade down parent-child boundaries:
- Superclass (Base Class): Contains shared states and routines.
- Subclass (Derived Class): Inherits public/protected members, and can override behaviors or add properties.
- Constructor Chaining: When instantiating a subclass, the parent constructor executes first (using
super()in Java) to initialize inherited attributes. - Single vs Multiple Inheritance: Java restricts classes to single inheritance to prevent the Diamond Problem (resolving conflicts when inheriting identical methods from two parents). C++ supports multiple inheritance directly.
5. Visual Diagrams (UML & Memory structures)
Class Diagram
Object Diagram
baseSalary = 5000.00 (inherited)
bonus = 1000.00
Memory Diagram
Stack (Reference)
Heap space (Subclass Object Layout)
Type: FullTimeEmployee
[Parent fields]
name: "Alice"
baseSalary: 5000.00
[Child fields]
bonus: 1000.00
Object Lifecycle
6. Syntax Explanation
- Java: Uses
extendsto establish inheritance. Accesses parent constructor usingsuper(...). - Python: Passes the base class name inside subclass parameters:
class FullTimeEmployee(Employee):. Calls parent constructor viasuper().__init__(...). - C++: Declares accessibility namespaces during inheritance:
class FullTimeEmployee : public Employee. Passes variables up to base constructors inside initialization lists.
7. Step-by-Step Implementation
Let's build a hierarchical Employee Payroll System:
- Step 1: Declare base class
Employeewith protected variablesnameandbaseSalary. - Step 2: Declare a constructor initializing both base variables.
- Step 3: Build a subclass
FullTimeEmployeeextendingEmployeeand declaring a subclass fieldbonus. - Step 4: Invoke
super(...)inside the subclass constructor to link instantiation. - Step 5: Override the
calculatePay()method inside the subclass to combine salary and bonus values.
8. Complete Code (Mini Project)
9. Code Walkthrough
FullTimeEmployee inherits name and baseSalary properties from the base class Employee. During initialization, super(name, baseSalary) runs the parent constructor. The @Override annotation flags the compiler to route calculations to the child's method at runtime.
10. Execution Flow
- Call subclass constructor.
- Trigger base parent class constructor to map base variables on the heap.
- Subclass constructor binds child attributes.
- Call
calculatePay(), yielding combined values polymorphically.
11. Internal Working
In memory, the subclass object does not contain two nested instances. The JVM allocates one flat composite memory block in the Heap that contains both the parent's fields (name, baseSalary) and the child's fields (bonus).
12. Complexity Analysis
- Time Complexity: $O(1)$ to instantiate objects and access fields.
- Space Complexity: $O(1)$ constant allocation per object.
13. Best Practices
- Favor Composition over Inheritance: Use inheritance only when a strict, permanent "Is-A" relationship is present. Otherwise, use composition ("Has-A").
- Keep hierarchies shallow: Keep class hierarchies to 2 or 3 levels maximum. Deep hierarchies are difficult to maintain.
14. Common Mistakes
- Violating the Liskov Substitution Principle (LSP) by changing the expected behavior of base methods in a way that breaks client code.
- Inheriting from a class just to reuse its helper methods when no conceptual "Is-A" link exists.
15. Interview Questions
Q: What is the Diamond Problem, and how does Java avoid it?
Answer: The Diamond Problem occurs in multiple inheritance when a class inherits from two classes that both inherit from a single base class. If both parent classes override a method, the subclass doesn't know which one to run. Java avoids this by supporting only single class inheritance.
16. Practice Exercises
- Easy: Add a subclass
Contractorthat calculates pay based on hours worked and an hourly rate. - Medium: Add validation inside the
Employeeconstructor to prevent null names or negative base salaries. Ensure this validation runs when subclasses are instantiated. - Hard: Redesign the system to calculate pay using composition (a
PayrollStrategyclass injected at runtime) instead of class inheritance, comparing the trade-offs of both designs.
17. Challenge Problem
Design a vehicle rental hierarchy (VEHICLE base, CAR child, TRUCK child) with dynamic surcharge computations based on vehicle weight and dimensions.
18. Summary
- Inheritance establishes an "Is-A" relationship between classes.
- Constructor chaining ensures parent attributes are initialized before subclass execution.
- Subclasses are stored as flat, single objects containing both parent and child fields in heap memory.
19. Cheat Sheet
| Relationship Type | Conceptual Link | Coupling Level |
|---|---|---|
| Inheritance | Is-A relationship (SportsCar is a Car) | Tightly Coupled |
| Composition | Has-A relationship (Car has an Engine) | Loosely Coupled |
20. Quiz
1. Which relationship type represents class inheritance?
A) Has-A
B) Is-A (Correct)
C) Uses-A
2. Which constructor runs first when instantiating a subclass?
A) Subclass constructor
B) Superclass constructor (Correct)
C) Constructors run in parallel threads
3. How does Java resolve the Diamond Problem in multiple class inheritance?
A) Using interfaces instead of abstract classes
B) Restricting class extension to single inheritance only (Correct)
C) By picking the first class declared in the classpath
4. What is constructor chaining?
A) Calling a constructor from within another constructor (Correct)
B) Executing destructors recursively
C) Re-instantiating null pointers
5. How are parent and child fields laid out in heap memory when instantiating a subclass?
A) Allocated in two separate heap blocks pointing to each other
B) Allocated in a single flat memory block containing both parent and child fields (Correct)
C) Allocated inside stack frames
6. What keyword is used in Java subclasses to call parent methods or constructors?
A) base
B) super (Correct)
C) this
7. What is the access modifier that grants access only to subclasses and the package?
A) private
B) protected (Correct)
C) default
8. Which OOP rule requires subclass objects to be substitute-able for base parent objects?
A) Single Responsibility Principle
B) Liskov Substitution Principle (Correct)
C) Dependency Inversion Principle
9. In C++, why must base class destructors be declared virtual?
A) To prevent memory leaks when deleting subclass objects via base pointers (Correct)
B) To speed up program termination checks
C) To enable multi-inheritance
10. What does subclass method overriding replace?
A) The parent method signature
B) The execution of the parent method at runtime (Correct)
C) The compile checks
21. Next Lesson Preview
In the next lesson, we will explore Polymorphism to master both compile-time method overloading and runtime method overriding!