Understanding Different Types of Inheritance

Understanding Different Types of Inheritance

19 Oct 2024
Intermediate
879K Views
44 min read
Learn with an interactive course and practical hands-on labs

Java Programming Course

Different Types of Inheritance

Inheritance is a key concept for developers looking to simplify class structures and enhance code reusability. It allows you to extend the functionality of existing classes without duplicating code, enabling the creation of hierarchical relationships between classes. Through inheritance, you can define and inherit properties and methods, supporting polymorphism and encapsulation.

In this OOPs tutorial, I’ll cover the various types of inheritance, how they improve class design by promoting code reuse and flexibility, and why mastering inheritance is vital for building scalable and maintainable applications.

What is Inheritance?

  • Inheritance lets you create a new class (child) that gets features from an existing class (parent).
  • It helps you reuse code and allows the child class to add or change things.
  • You can even change how some features work.
  • There are different types of inheritance, like single inheritance (one parent) and multiple inheritance (using interfaces).

What is Inheritance in Java?

Let's understand with a simple example.

  • In the example, aVehicle is a base class that shares characteristics and methods with its derived classes, such as Cars, Buses, Trucks, and Bikes.
  • Each of these derived classes inherits a Vehicle's traits while adding their own characteristics, such as passenger capacity for a bus or load capacity for a truck.
  • This format encourages code reuse and facilitates code maintenance.
Read More: Top 50 OOPs Interview Questions and Answers

Different Types of Inheritance

OOP supports six different types of inheritance, as given below:

  1. Single Inheritance
  2. Multi-level Inheritance
  3. Multiple Inheritance
  4. Multipath Inheritance
  5. Hierarchical Inheritance
  6. Hybrid Inheritance

Different Types of Inheritance

1. Single Inheritance

  • Single inheritance is when a class inherits from only one parent class.
  • This setup allows the child class to use the properties and methods of its single parent, making the code easier to manage.
  • In single inheritance, the child class can add new features or change how existing features work from the parent class.
  • This keeps the design simple and clear, helping you understand the relationships between classes better.
  • Using single inheritance promotes a straightforward structure, making your code modular and easier to maintain.
  • It helps create a clean hierarchy where each subclass builds on the functionality of its parent without unnecessary complexity.

Single Inheritance

In the given figure, Class A is the parent class, and Class B is the child class since Class B inherits the features and behavior of the parent class A.

Example


#include <iostream>
using namespace std;

class Animal {
public:
    void makeSound() {
        cout << "The animal makes a sound." << endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        cout << "The dog barks." << endl;
    }
};

int main() {
    Dog myDog;
    myDog.makeSound();  // Call base class method
    myDog.bark();       // Call derived class method
    return 0;
}
            

using System;

class Animal
{
    public void MakeSound()
    {
        Console.WriteLine("The animal makes a sound.");
    }
}

class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("The dog barks.");
    }
}

class Program
{
    static void Main()
    {
        Dog myDog = new Dog();
        myDog.MakeSound(); // Call base class method
        myDog.Bark();      // Call derived class method
    }
}
            

class Animal:
    def make_sound(self):
        print("The animal makes a sound.")

class Dog(Animal):
    def bark(self):
        print("The dog barks.")

my_dog = Dog()
my_dog.make_sound()  # Call base class method
my_dog.bark()        # Call derived class method
            

class Animal {
    void makeSound() {
        System.out.println("The animal makes a sound.");
    }
}

class Dog extends Animal {
    void bark() {
        System.out.println("The dog barks.");
    }
}

public class Main {
    public static void main(String[] args) {
        Dog myDog = new Dog();
        myDog.makeSound(); // Call base class method
        myDog.bark();      // Call derived class method
    }
}
            

class Animal {
    makeSound() {
        console.log("The animal makes a sound.");
    }
}

class Dog extends Animal {
    bark() {
        console.log("The dog barks.");
    }
}

let myDog = new Dog();
myDog.makeSound(); // Call base class method
myDog.bark();      // Call derived class method
            

Output


The animal makes a sound.
The dog barks.
    

Explanation

  • In this code, you create a base class called Animal with a method to make a sound.
  • Then, you create a Dog class that inherits from Animal, meaning it can use the makeSound() method.
  • When you create a Dog object, you can call both the sound-making method from the base class and the barking method from the derived class.
  • It is like having a general animal and a specific dog that can do everything the animal can, plus more.

Read More: Inheritance in C++ with Modifiers

2. Multi-level inheritance

  • Multi-level inheritance involves a class inheriting from a derived class, creating a chain of inheritance.
  • This structure allows for a hierarchy of classes, where each class can build upon the features of its parent class and extend its functionality to its own subclasses.
  • In this setup, each level can introduce unique methods and properties while maintaining access to the features of the classes above it.
  • For example, a base class can provide common attributes, a derived class can add specialized behavior, and a further subclass can enhance or modify that behavior.
  • This approach promotes organized code and facilitates the creation of complex systems while keeping the design clean.
  • Multi-level inheritance enhances code reuse and simplifies the management of relationships between different classes.

In the given figure, class C inherits the properties and behavior of class B, and class B inherits from class A. So, here, A is the parent class of B, and class B is the parent class of C. So, here, class C implicitly inherits the properties and behavior of Class A along with Class B, i.e., there is a multilevel inheritance.

Example


#include <iostream>
using namespace std;

class Animal {
public:
    void makeSound() {
        cout << "The animal makes a sound." << endl;
    }
};

class Dog : public Animal {
public:
    void bark() {
        cout << "The dog barks." << endl;
    }
};

class Puppy : public Dog {
public:
    void weep() {
        cout << "The puppy weeps." << endl;
    }
};

int main() {
    Puppy myPuppy;
    myPuppy.makeSound();  // From Animal class
    myPuppy.bark();       // From Dog class
    myPuppy.weep();       // From Puppy class
    return 0;
}
            

Output


The animal makes a sound.
The dog barks.
The puppy weeps.
    

Explanation

  • In this code, you have a base class called Animal with a method that makes a sound.
  • The dog class inherits from animals and adds its own method of barking.
  • Then, the Puppy class inherits from the Dog, allowing it to use both the sound-making and barking methods.
  • When you create a puppy object, you can use all three methods: makeSound() from the Animal class, bark() from the Dog class, and weep() from the Puppy class.
  • It is like having a general animal, a specific dog, and an even younger puppy that can do everything the dog can do, plus its own special action.

Read More: What is Inheritance in Java: Types of Inheritance in Java

3. Multiple inheritance

  • Multiple inheritance allows a class to inherit features and behaviors from more than one parent class.
  • This means a derived class can combine properties from multiple sources, providing more flexibility in design.
  • When a class inherits from multiple parent classes, it can access methods and attributes from all of them, which enhances functionality.
  • However, this can also create challenges, such as the "diamond problem," where ambiguity arises if two parent classes have methods with the same name.
  • Using interfaces is a common way to implement multiple inheritance, as they allow classes to share behavior without the complications of state.
  • This approach promotes modular design and encourages code reuse.
  • Multiple inheritance is useful for creating classes that need to incorporate features from different sources, making your code more versatile and adaptable to various situations.

Multiple Inheritance

In the given figure, class C inherits the properties and behavior of classes B and A at the same level. So, here, A and B are both the parent classes for Class C.

Example


#include <iostream>
using namespace std;

// Interface for barking
class ICanBark {
public:
    virtual void Bark() = 0;
};

// Interface for running
class ICanRun {
public:
    virtual void Run() = 0;
};

// Class implementing both interfaces
class Dog : public ICanBark, public ICanRun {
public:
    void Bark() override {
        cout << "The dog barks." << endl;
    }
    
    void Run() override {
        cout << "The dog runs." << endl;
    }
};

int main() {
    Dog myDog;
    myDog.Bark(); // From ICanBark
    myDog.Run();  // From ICanRun
    return 0;
}
            

Output


The dog barks.
The dog runs.
    

Explanation

  • This program demonstrates multiple inheritance using interfaces.
  • The `ICanBark` and `ICanRun` interfaces declare the `Bark` and `Run` methods, respectively.
  • The `Dog` class implements both interfaces.
  • The `Dog` class provides definitions for the `Bark` and `Run` methods.
  • In the `main` function, an instance of `Dog` calls both methods, displaying appropriate messages.

4. Multipath Inheritance

  • Multipath inheritance occurs when a class inherits from multiple parent classes, creating different paths to reach the same parent.
  • This means that a child class can access methods from more than one parent class, leading to a more flexible design.
  • In multipath inheritance, it’s important to manage potential conflicts, such as when multiple parent classes define the same method.
  • This can lead to ambiguity about which method should be called, making the code harder to understand.
  • While multipath inheritance can promote code reuse and flexibility, it also adds complexity to the class hierarchy.
  • Careful planning and clear structure are essential to avoid confusion and maintain a clean design.

Multipath Inheritance

In the given figure, class D inherits the properties and behavior of classes C and B. Both class C and class B inherit Class A. So, Class A is the parent for Class B and Class C as well as Class D. So it's making a Multipath inheritance.

Example


#include <iostream>
using namespace std;

// Base interface
class IAnimal {
public:
    virtual void MakeSound() = 0;
};

// Derived interface 1
class IDog : public IAnimal {
public:
    virtual void Bark() = 0;
};

// Derived interface 2
class IPet : public IAnimal {
public:
    virtual void Play() = 0;
};

// Class implementing both derived interfaces
class Puppy : public IDog, public IPet {
public:
    void MakeSound() override {
        cout << "The puppy makes a sound." << endl;
    }
    
    void Bark() override {
        cout << "The puppy barks." << endl;
    }
    
    void Play() override {
        cout << "The puppy plays." << endl;
    }
};

int main() {
    Puppy myPuppy;
    myPuppy.MakeSound(); // From IAnimal
    myPuppy.Bark();      // From IDog
    myPuppy.Play();      // From IPet
    return 0;
}
            

Output


The puppy makes a sound.
The puppy barks.
The puppy plays.
    

Explanation

  • This program demonstrates multiple inheritance using interfaces.
  • The base interface `IAnimal` declares the `MakeSound` method.
  • Two derived interfaces, `IDog` and `IPet`, extend `IAnimal` with the `Bark` and `Play` methods, respectively.
  • The `Puppy` class implements both derived interfaces.
  • In the `main` function, an instance of `Puppy` calls the `MakeSound`, `Bark`, and `Play` methods, displaying appropriate messages.

5. Hierarchical Inheritance

  • Hierarchical inheritance occurs when multiple child classes inherit from a single-parent class.
  • This structure allows different child classes to share the properties and methods of the same parent, promoting code reuse and organization.
  • In hierarchical inheritance, each child class can implement its own unique features while still benefiting from the common functionality provided by the parent class.
  • This approach is useful for creating a clear relationship between related classes and simplifying the code structure.
  • However, it’s important to ensure that the parent class is designed to accommodate the needs of all its child classes, which may require careful planning.
  • Overall, hierarchical inheritance provides a straightforward way to build a class hierarchy while maintaining code efficiency.

Hierarchical Inheritance

In the figure given, class C has two children, class A and class B.

Example


#include <iostream>
using namespace std;

// Base class
class Animal {
public:
    void MakeSound() {
        cout << "The animal makes a sound." << endl;
    }
};

// Derived class Dog
class Dog : public Animal {
public:
    void Bark() {
        cout << "The dog barks." << endl;
    }
};

// Derived class Cat
class Cat : public Animal {
public:
    void Meow() {
        cout << "The cat meows." << endl;
    }
};

int main() {
    Dog myDog;
    myDog.MakeSound(); // From Animal class
    myDog.Bark();      // From Dog class

    Cat myCat;
    myCat.MakeSound(); // From Animal class
    myCat.Meow();      // From Cat class

    return 0;
}
            

Output


The animal makes a sound.
The dog barks.
The animal makes a sound.
The cat meows.
    

Explanation

  • This program shows single inheritance using two derived classes: Dog and Cat.
  • The base class Animal gives you the MakeSound method.
  • The Dog class inherits from Animal and adds its own Bark method.
  • Similarly, the Cat class inherits from Animal and includes a Meow method.
  • In the main function, you create instances of Dog and Cat, allowing them to call their specific methods, as well as the inherited MakeSound method from the Animal class.

6. Hybrid Inheritance

  • Hybrid inheritance combines different inheritance types, like single, multiple, and hierarchical, within one structure.
  • This allows you to use various features and functionalities from multiple parent classes, creating a versatile and reusable codebase.
  • In this approach, classes can inherit from multiple sources, which helps in sharing common traits among related classes.
  • This flexibility promotes efficient code organization and reduces redundancy.
  • However, hybrid inheritance can lead to complexity, especially with issues like the "diamond problem," where a class inherits from two classes that have a common ancestor.
  • Therefore, careful design is crucial to manage potential ambiguities and maintain code clarity.
  • Overall, hybrid inheritance offers a robust way to utilize diverse inheritance patterns for more intricate applications.
Hybrid Inheritance

Since .NET Languages like C#, F#, etc., do not support multiple and multipath inheritance. Hence, hybrid inheritance with a combination of multiple or multipath inheritances is not supported by .NET Languages.

Example


#include <iostream>
using namespace std;

// Base interface
class IAnimal {
public:
    virtual void MakeSound() = 0;
};

// Derived interface 1
class IDog : public IAnimal {
public:
    virtual void Bark() = 0;
};

// Another interface for pets
class IPet {
public:
    virtual void Play() = 0;
};

// Class implementing IDog and IPet (Hybrid Inheritance)
class Puppy : public IDog, public IPet {
public:
    void MakeSound() override {
        cout << "The puppy makes a sound." << endl;
    }

    void Bark() override {
        cout << "The puppy barks." << endl;
    }

    void Play() override {
        cout << "The puppy plays." << endl;
    }
};

int main() {
    Puppy myPuppy;
    myPuppy.MakeSound(); // From IAnimal
    myPuppy.Bark();      // From IDog
    myPuppy.Play();      // From IPet

    return 0;
}
            

using System;

// Base interface
interface IAnimal
{
    void MakeSound();
}

// Derived interface 1
interface IDog : IAnimal
{
    void Bark();
}

// Another interface for pets
interface IPet
{
    void Play();
}

// Class implementing IDog and IPet interfaces (Hybrid Inheritance)
class Puppy : IDog, IPet
{
    public void MakeSound()
    {
        Console.WriteLine("The puppy makes a sound.");
    }

    public void Bark()
    {
        Console.WriteLine("The puppy barks.");
    }

    public void Play()
    {
        Console.WriteLine("The puppy plays.");
    }
}

class Program
{
    static void Main()
    {
        Puppy myPuppy = new Puppy();
        myPuppy.MakeSound(); // From IAnimal
        myPuppy.Bark();      // From IDog
        myPuppy.Play();      // From IPet
    }
}
            

from abc import ABC, abstractmethod

# Base interface
class IAnimal(ABC):
    @abstractmethod
    def make_sound(self):
        pass

# Derived interface 1
class IDog(IAnimal):
    @abstractmethod
    def bark(self):
        pass

# Another interface for pets
class IPet(ABC):
    @abstractmethod
    def play(self):
        pass

# Class implementing IDog and IPet (Hybrid Inheritance)
class Puppy(IDog, IPet):
    def make_sound(self):
        print("The puppy makes a sound.")

    def bark(self):
        print("The puppy barks.")

    def play(self):
        print("The puppy plays.")

my_puppy = Puppy()
my_puppy.make_sound()  # From IAnimal
my_puppy.bark()        # From IDog
my_puppy.play()        # From IPet
            

interface IAnimal {
    void makeSound();
}

interface IDog extends IAnimal {
    void bark();
}

interface IPet {
    void play();
}

// Class implementing both IDog and IPet interfaces (Hybrid Inheritance)
class Puppy implements IDog, IPet {
    public void makeSound() {
        System.out.println("The puppy makes a sound.");
    }

    public void bark() {
        System.out.println("The puppy barks.");
    }

    public void play() {
        System.out.println("The puppy plays.");
    }
}

public class Main {
    public static void main(String[] args) {
        Puppy myPuppy = new Puppy();
        myPuppy.makeSound(); // From IAnimal
        myPuppy.bark();      // From IDog
        myPuppy.play();      // From IPet
    }
}
            

class IAnimal {
    makeSound() {
        throw "Method not implemented.";
    }
}

class IDog extends IAnimal {
    bark() {
        throw "Method not implemented.";
    }
}

class IPet {
    play() {
        throw "Method not implemented.";
    }
}

class Puppy extends IDog {
    makeSound() {
        console.log("The puppy makes a sound.");
    }

    bark() {
        console.log("The puppy barks.");
    }

    play() {
        console.log("The puppy plays.");
    }
}

const myPuppy = new Puppy();
myPuppy.makeSound(); // From IAnimal
myPuppy.bark();      // From IDog
myPuppy.play();      // From IPet
            

Output


The puppy makes a sound.
The puppy barks.
The puppy plays.
    

Explanation

  • This program demonstrates hybrid inheritance using interfaces.
  • The base interface `IAnimal` declares the `MakeSound` method.
  • The derived interface `IDog` extends `IAnimal` with the `Bark` method.
  • Another interface, `IPet,` adds the `Play` method.
  • The `Puppy` class implements both the `IDog` and `IPet` interfaces.
  • In the `Main` method, an instance of `Puppy` calls the methods defined in both interfaces.

Advantages of Inheritance

  1. Reduce code redundancy.
  2. Provides better code reusabilities.
  3. Reduces source code size and improves code readability.
  4. The code is easy to manage and divided into parent and child classes.
  5. Supports code extensibility by overriding the base class functionality within child classes.
  6. Code usability will eventually enhance reliability, and the base class code will always be tested and debugged against the issues.

Disadvantages of Inheritance

  1. In inheritance, both base and child classes are tightly coupled. Hence, If you change the code of the parent class, it will affect all the child classes.
  2. In a class hierarchy, many data members remain unused, and the memory allocated is not utilized. Hence, it affects the performance of your program if you have not implemented inheritance correctly.
  3. Inheritance increases the coupling between the base class and the derived class. Any small change in the base class will directly affect all child classes that are extending the parent class.

Read More:

Summary

Inheritance is a key concept in object-oriented programming that allows one class to inherit properties and methods from another, promoting code reusability and organization. This article explores the various types of inheritance, including single, multi-level, multiple, multipath, hierarchical, and hybrid inheritance, along with their advantages and disadvantages. Understanding these concepts is essential for designing scalable and maintainable applications. To master more OOPS concepts and other programming language concepts, enroll in Scholarhat's Full-Stack Java Developer Certification Training Course.

FAQs

Method Overriding: It is when you create a new version of a method in a derived class that already exists in the base class. You change how it works in the derived class.
Method Overloading: It is when you have multiple methods in the same class with the same name but different parameters. It is like having different ways to do something with the same name.

No, a derived class cannot directly use private members of the base class. It is like trying to reach something that is locked away. However, it can access public or protected methods that may use those private members.

The diamond problem happens when a class inherits from two classes that share a common base class. It is like having two paths to the same place, and it can get confusing about which path to take. Some programming languages have rules to avoid this problem.
Share Article
About Author
Shailendra Chauhan (Microsoft MVP, Founder & CEO at Scholarhat by DotNetTricks)

Shailendra Chauhan is the Founder and CEO at ScholarHat by DotNetTricks which is a brand when it comes to e-Learning. He provides training and consultation over an array of technologies like Cloud, .NET, Angular, React, Node, Microservices, Containers and Mobile Apps development. He has been awarded Microsoft MVP 9th time in a row (2016-2024). He has changed many lives with his writings and unique training programs. He has a number of most sought-after books to his name which has helped job aspirants in cracking tough interviews with ease.
Accept cookies & close this