Inheritance in java
1. Inheritance
Inheritance is the process of extending the functionality of a class by defining a new class that inherits the features of the existing class and adds some additional features. Inheritance is used for code reusability and run time polymorphism.
Syntax of extending a class:
class SubClass extends SuperClass{
}
Super-class is the class that is being extended.
Sub-class is the class that extends the super-class.
Note: Only non-static data members and methods of the super-class can be extended by the sub-class.
A super-class can be extended by any number of classes, but a sub-class can only extend a single class, i.e. multiple inheritances are not allowed in java directly. Although it can be achieved by using interfaces.
When a class extends another class then an IS-A relation is created between them.
Take a look at the example below:
Super-class
class Employee{
int salary;
public Employee(int salary){
this.salary = salary;
}
}
Sub-class
class Programmer extends Employee{
String name;
public Programmer(String name, int salary){
super(salary);
this.name = name;
}
}
EmployeeTest.java
class EmployeeTest{
public static void main(String arr[]){
Programmer p = new Programmer("abc", 500000);
System.out.println("Name of the programmer is : "+p.name);
System.out.println("Salary of the programmer is : "+p.salary);
}
}
Output:
Name of the programmer is : abc
Salary of the programmer is : 500000
As you can see in the above example that we were able to access the salary property from the Programmer object and also we were able to call the constructor of the super-class using the super(salary);
in the constructor of Programmer.java
. This all possible because the programmer class extended the employee class so the properties of the employee class were available in the programmer class.
Note: Learn more about the super()
keyword in detail here.
2. Code reusability in inheritance
One of the main features of inheritance is the ability to reuse the code of the super-class. Let's understand with the example below:
Rectangle.java
class Rectangle{
int length, breadth;
public Rectangle(int l, int b){
length = l;
breadth = b;
}
public void display(){
System.out.println("Length : "+length);
System.out.println("Breadth : "+breadth);
}
}
Cuboid.java
class Cuboid extends Rectangle{
//additional attribute
int height;
public Cuboid(int l, int b, int h){
super(l, b);
height = h;
}
public void display(){
//code reusability
super.display();
//additional behavior
System.out.println("Height : "+height);
}
}
Test.java
class Test{
public static void main(String arr[]){
Cuboid c = new Cuboid(9,6,3);
c.display();
}
}
Output:
Length : 9
Breadth : 6
Height : 3
In the above example, we reused the code written in display()
method of Rectangle.java
to print the length and breadth of the cuboid.
3. Runtime polymorphism using inheritance
Runtime polymorphism is the process of resolving a method call at the time of execution, i.e. which method to call is decided while the method is in running state. Runtime polymorphism is achieved with the help of method overriding. Let's first understand what is method overriding.
3.1. Method overriding
When a method that is present in the super-class is also defined by the subclass with the same signature, then it is known as method overriding. Take a look at the below example
Shape.java
class Shape{
public void display(){
System.out.println("This is a shape");
}
}
Circle.java
class Circle extends Shape{
public void display(){
System.out.println("This is a circle");
}
}
ShapeTest.java
class ShapeTest{
public static void main(String arr[]){
Circle c = new Circle();
c.display();
}
}
Output:
This is a circle
In this case, the display()
method of Circle.java
is invoked instead of Shape.java
because the display()
method was overridden by the Circle.java
. If display()
were not overridden, then display()
of Shape.java
would have invoked.
3.2. Implementing runtime polymorphism
Let's see how we can achieve runtime polymorphism using method overriding.
Animal.java
class Animal{
public void makeSound(){
System.out.println("Animal is making sound");
}
}
Dog.java
class Dog extends Animal{
public void makeSound(){
System.out.println("Bow Bow ...");
}
}
Cat.java
class Cat extends Animal{
public void makeSound(){
System.out.println("Meow Meow ...");
}
}
AnimalTest.java
class AnimalTest{
public static void main(String arr[]){
Animal animal = new Animal();
Animal dog = new Dog();
Animal cat = new Cat();
doSomething(animal);
doSomething(dog);
doSomething(cat);
}
public static void doSomething(Animal animal){
animal.makeSound();
}
}
Here you can see we created 3 different types of objects with the same type of reference variable. The method call to makeSound()
will be resolved at runtime.
Output:
Animal is making sound
Bow Bow ...
Meow Meow ...
Since doSomething()
method can take an argument of type Animal
we were able to pass the dog and cat objects to it as they both are of type Animal
and because of the concept of method overriding the makeSound()
method of cat and dog class was invoked.
This is how you achieve runtime polymorphism. In the doSomething()
method you can even pass the objects of classes which will be created sometime in the future if they all fulfill the condition of extending the Animal
class.
4. Conclusion
In this tutorial, we saw what is inheritance and how we can use it in java programming to achieve code reusability and runtime polymorphism.