Inheritance in OOP

Rashmi Sandamini
7 min readNov 1, 2023

--

In our previous article, we explored the basics of Object Oriented Programming (OOP). We looked at how today’s modern software is built and organized. In this article, let’s explore ‘inheritance’, one of the core concepts in OOP.

What is Inheritance?

Inheritance is a mechanism that allows us to create hierarchies of classes, facilitating the sharing of properties and methods among them. ‘Inheritance’ is a concept that shows the idea of code reusability and extensibility, enabling to make efficient and flexible software solutions. To avoid code repetition, inheritance can be used. But it is not the only way to do that.

Important terminologies:

Parent class — The class whose properties and methods are inherited, which is also known as superclass or base class.

Child class — The class that inherits properties and methods are called child class, subclass or derived class.

Inheritance presents the Is-A relationship which can be seen in everyday objects. For example, “Dog Is A Animal” makes perfect sense, with ‘Animal’ as the parent class and ‘Dog’ as the child class.

Let’s dive deeper into this concept. We can use ‘Animal’ as the parent class and include common properties like name, color, and age. Then, we can create subclasses, such as ‘Dog,’ ‘Cat,’ ‘Horse,’ and so on, to inherit these properties.

Let’s see this example using Java in a simple manner.

class Animal {
public String name;
public int age;
public void eat(){
System.out.println("Animal is eating...");
}
}

//inherit from Animal
class Dog extends Animal{
public String barkSound;
public void bark(){
System.out.println("Dog is barking...");
}

// Display dog-specific properties.
public void display(){
System.out.println("Name: "+name+"\nAge: "+age);
}
}

class Test{
public static void main(String[] args) {

//create an object of the child class
Dog dog =new Dog();

//accessing properties of the parent class
dog.name="Browniee";
dog.age=8;

//accessing properties of the own child class
dog.barkSound="Yappy";
dog.display();
dog.bark();

// call the method in parent class using child class object
dog.eat();

}
}

Output:

Name: Browniee
Age: 8
Dog is barking…
Animal is eating…

“In the example above, we have two classes: Animal and Dog. The Animal class includes common properties such as name and age, along with a method called eat(). The Dog class, which inherits from the Animal class, introduces its specific property, barkSound, and two methods, bark() and display(), the latter being used to display the properties of the Dog class.

Then we created an instance of the Dog class, referred to as dog. By accessing the properties of the parent class, namely name and age, we demonstrate that even though these properties are not directly defined in the Dog class, we can still access and assign values to them. This shows the fact that the Dog class inherits all properties and methods from its parent class, Animal. This exemplifies the key principle of inheritance, where properties and methods are passed down from the parent class to its subclasses.

Types of Inheritance

There are different types of inheritance as follows.

  1. Single Inheritance — A single child class extends from only a single parent class. In the below diagram, Class A is the parent class and the Class B is the child class, where Class B only extends from Class A.
Single Inheritance

2. Multilevel Inheritance — One class can inherit from a child class and that child class becomes the parent class for the new class.

Multilevel Inheritance

3. Multiple Inheritance — A single child class extends from multiple parent classes.

Multiple Inheritance

4. Hierarchical Inheritance — More than one child classes extends from a single parent class.

Hierarchical Inheritance

It is important to note that Java does not support multiple inheritance with classes, but is supported by Java interfaces. Multiple inheritance in Java with classes creates a problem called ‘Diamond Problem’. Let me explain it using an example.

Diamond Problem

Consider the above example. There, Class A is the superclass and has a method called display() and the subclasses of Class A which are Class B and Class C overrides the display() method. So, when an object from the Class D invokes the method display() , the compiler gets confused which method to be executed as Class D extends both from Class B and Class C . This creates an ambiguity and results in a compile time error. This is the Diamond Problem.

Typecasting in Inheritance

Typecasting in inheritance refers to the process of converting object reference from one class type to another class type within the inheritance hierarchy. There are two type of typecasting .

Upcasting — In the upcasting, we can create a child class object and assign it to a parent class variable and treat that as if it were an instance of its parent class. This process is safe and implicit because the child class inherits all the attributes and behaviors of the parent class. Let’s see an example.

class Animal {
public void eat(){
System.out.println("Animal is eating...");
}
}
class Dog extends Animal{
@Override
public void eat(){
System.out.println("Dog is eating...");
}
}
class Test{
public static void main(String[] args) {
//creating a parent class object, referencing to the child class
Animal animal = new Dog();
animal.eat();

}
}

Output:

Dog is eating...

Here, we have called the method in the parent class (Animal class) but referred to the object of the child class. Since the method is overridden in the Dogclass, it invokes the overridden method. This behavior is a result of polymorphism, where the method called depends in the actual type of the object at runtime, not the reference type. (Don’t worry, we’ll learn about polymorphism in detail soon…). In upcasting, we can only access the properties of the parent class.

Downcasting — In downcasting, we convert a parent class reference to a child class reference. This is a somewhat risky operation and requires explicit casting. Let’s see an example.

class Test{
public static void main(String[] args) {
//creating a parent class object
//referencing to the child class - upcasting
Animal animal = new Dog();
//downcasting
Dog dog = (Dog) animal;

//trying to downcast -> throws a compile time error
Dog dog2= new Animal();

}
}

Above example shows the process to perform the downcasting without causing any errors. If we try to downcast implicitly, the compiler will throw an error.

Constructors in Inheritance

Constructors in inheritance play a crucial role in ensuring that objects of parent and child classes are properly initialized.

So far, in our examples we did not include any constructors to our classes, in those cases, a default constructor with no arguments is automatically provided. This default constructor is called when an object is created without specifying any constructor.

Parent class may have more than one constructors. When a child class is instantiated, its constructor implicitly calls the constructor of the parent class and then only the constructor call of the child class happens. This is explained in the below code.

class Animal {
public Animal() {
System.out.println("I am the Animal Class");
}
}

class Dog extends Animal {
public String barkSound;

// Constructor for Dog class
public Dog(String barkSound) {
this.barkSound = barkSound;
System.out.println("I am the Dog Class");
}
}

class Test {
public static void main(String[] args) {
Dog dog =new Dog("Yappy");
}
}

I am the Animal Class
I am the Dog Class

As you can see from the above example, even though we are not calling the constructor of the Animal class, the print statement of the Animal class is printed first. That is because the constructor of the parent class is automatically invoked when the object of the child class is created.

Since the constructors are not members, they do not inherit to its child classes. But, the constructor of the parent class can be called from a child class implicitly or using super keyword. But calling super keyword is only necessary if we have constructor parameters in the parent class. Let’s see this using our Animal example.

class Animal {
public String name;
public int age;

public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}

class Dog extends Animal {
public String barkSound;

// Constructor for Dog class
public Dog(String name, int age, String barkSound) {
super(name, age); //call the parent class constructor
this.barkSound = barkSound;
}
}

class Test {
public static void main(String[] args) {
Dog dog =new Dog("Browniee",8,"Yappy");
}
}

In the above example, in the Dog class constructor, the super(name,age) statement is used to call the constructor of the parent class Animal to set up the inherited properties. In the Test class, we instantiated the Dog class with the values for name , age , and barkSound .

It is also important to provide the exact number and the types of parameters specified in the parent class constructor declaration when calling that from the child class constructor. Otherwise, compilation error will occur.

Now that we have reached the end of this article. In this article, we talked about ‘inheritance’ in detail with examples to help us understand what these concepts actually mean. I hope this will help you to understand the concepts. Let’s meet in another article to explore another OOP concept like this 😊.

--

--