Inheritance in OOP
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.
- 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.
2. Multilevel Inheritance — One class can inherit from a child class and that child class becomes the parent class for the new class.
3. Multiple Inheritance — A single child class extends from multiple parent classes.
4. Hierarchical Inheritance — More than one child classes extends from a single parent class.
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.
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 Dog
class, 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 😊.
Thank you for reading my blog post. Happy Learning and Fun Coding!!!
Sources:
https://www.enjoyalgorithms.com/blog/inheritance-in-java