Imagine you are designing a software system to manage different types of vehicles. Many vehicles share common features like having wheels, engines, and the ability to move. Instead of rewriting these features for every type of vehicle, wouldn't it be efficient to define them once and then build specific vehicle types on top of that? This is exactly what inheritance in Object-Oriented Programming (OOP) allows us to do.
Inheritance is a fundamental concept in OOP that enables a new class to acquire properties and behaviors (methods) from an existing class. This mechanism promotes code reuse and models real-world hierarchical relationships, making programs easier to write, understand, and maintain.
In this section, we will explore inheritance from the ground up, understand its types, see how it works in practice, and learn how it connects with other OOP concepts.
Inheritance is a mechanism where a new class (called the child class or derived class) is created from an existing class (called the parent class, superclass, or base class), inheriting its attributes (data members) and behaviors (methods).
This means the child class automatically has all the features of the parent class, and can also add new features or modify existing ones.
Think of inheritance like a family tree: children inherit traits from their parents but also have their own unique characteristics.
In the diagram above, the arrow points from the child class to the parent class, indicating that the child inherits from the parent.
Inheritance can take several forms depending on how classes are related. Understanding these types helps in designing clear and efficient class hierarchies.
graph TD A[Parent Class] B[Child Class] C[Another Child Class] D[Grandchild Class] E[Second Parent Class] F[Child with Multiple Parents] A --> B A --> C B --> D E --> F A --> F
Types of Inheritance:
Inheritance allows child classes to reuse code, but sometimes the child class needs to change or extend the behavior of the parent class. This is done through method overriding.
Method Overriding means defining a method in the child class with the same name and signature as in the parent class, so the child's version is called instead of the parent's.
Access specifiers control the visibility of class members (attributes and methods) and affect inheritance:
| Access Specifier | Visible in Parent Class | Inherited by Child Class | Accessible in Child Class |
|---|---|---|---|
| public | Yes | Yes | Yes |
| protected | Yes | Yes | Yes |
| private | Yes | No | No |
Note: Private members are not directly accessible or inherited by child classes, which helps protect sensitive data.
Vehicle with a method to display the number of wheels. Then create a child class Car that inherits from Vehicle and uses the inherited method. Step 1: Define the parent class Vehicle with a method displayWheels().
class Vehicle { int wheels = 4; void displayWheels() { System.out.println("Number of wheels: " + wheels); }} Step 2: Define the child class Car that inherits from Vehicle.
class Car extends Vehicle { // Inherits wheels and displayWheels()} Step 3: Create an object of Car and call the inherited method.
Car myCar = new Car();myCar.displayWheels(); // Output: Number of wheels: 4
Answer: The Car class successfully inherits the displayWheels() method from Vehicle, demonstrating single inheritance.
Animal with a method sound() that prints "Animal makes a sound", create a child class Dog that overrides the sound() method to print "Dog barks". Step 1: Define the parent class Animal with the method sound().
class Animal { void sound() { System.out.println("Animal makes a sound"); }} Step 2: Define the child class Dog that overrides the sound() method.
class Dog extends Animal { @Override void sound() { System.out.println("Dog barks"); }} Step 3: Create objects and call the sound() method.
Animal a = new Animal();a.sound(); // Output: Animal makes a soundDog d = new Dog();d.sound(); // Output: Dog barks
Answer: The Dog class overrides the sound() method to provide specific behavior, illustrating method overriding.
Printer and Scanner with methods print() and scan() respectively. Create a child class AllInOneMachine that inherits from both and uses both methods. Discuss potential ambiguity. Step 1: Define the two parent classes.
class Printer { void print() { System.out.println("Printing document"); }}class Scanner { void scan() { System.out.println("Scanning document"); }} Step 2: In languages like Java which do not support multiple inheritance with classes, use interfaces or other mechanisms. But for illustration, assume multiple inheritance is allowed.
class AllInOneMachine extends Printer, Scanner { // Inherits print() and scan()} Step 3: Use the AllInOneMachine object.
AllInOneMachine device = new AllInOneMachine();device.print(); // Output: Printing documentdevice.scan(); // Output: Scanning document
Potential Issue: If both parent classes have methods with the same name and signature, ambiguity arises about which method to call. This is known as the diamond problem and is a common pitfall in multiple inheritance.
Answer: Multiple inheritance allows combining features from multiple classes but can cause ambiguity, so it should be used carefully or avoided in some languages.
Animal is the parent class, Bird inherits from Animal, and Parrot inherits from Bird. Show how properties and methods are inherited across levels. Step 1: Define the base class Animal.
class Animal { void eat() { System.out.println("Animal eats"); }} Step 2: Define the intermediate class Bird inheriting from Animal.
class Bird extends Animal { void fly() { System.out.println("Bird flies"); }} Step 3: Define the child class Parrot inheriting from Bird.
class Parrot extends Bird { void speak() { System.out.println("Parrot speaks"); }} Step 4: Create a Parrot object and call all methods.
Parrot p = new Parrot();p.eat(); // Output: Animal eatsp.fly(); // Output: Bird fliesp.speak(); // Output: Parrot speaks
Answer: The Parrot class inherits methods from both Bird and Animal, demonstrating multilevel inheritance.
Person with public, protected, and private members, show which members are accessible in the child class Student. Step 1: Define the Person class.
class Person { public String name = "John"; protected int age = 30; private String ssn = "123-45-6789"; public void display() { System.out.println("Name: " + name); System.out.println("Age: " + age); System.out.println("SSN: " + ssn); }} Step 2: Define the child class Student and try to access members.
class Student extends Person { void show() { System.out.println("Name: " + name); // Accessible (public) System.out.println("Age: " + age); // Accessible (protected) // System.out.println("SSN: " + ssn); // Not accessible (private) - causes error }} Step 3: Explanation:
name is public, so accessible in Student.age is protected, so accessible in Student.ssn is private, so not accessible in Student.Answer: Only public and protected members are inherited and accessible in child classes; private members remain hidden.
When to use: When deciding if one class should inherit from another, ask: "Is this class a type of the other?" For example, a Car is a Vehicle.
When to use: When the child class needs to perform a task differently from the parent, override the method instead of rewriting the entire class.
When to use: To prevent ambiguity and complex bugs, prefer interfaces or composition over multiple inheritance.
When to use: Mark members as private if they should not be inherited or accessed directly, and protected if they should be accessible in child classes but hidden outside.
When to use: Visualizing inheritance relationships helps in understanding and debugging complex class structures.
super() in child classes.Progress tracking is paywalled — subscribe to mark subtopics as understood and save your streak.
Go to practice →