DEV Community

Subash
Subash

Posted on Edited on

Inheritance in Java, Polymorphism

What is Inheritance?

Inheritance is an OOP concept in Java that allows one class to acquire the properties and methods of another class.

It helps us reuse existing code instead of writing the same code again.

Why Do We Need Inheritance?

Consider a simple example.

We have two classes: Animal and Dog.

Both animals and dogs can eat. Instead of writing the eat() method separately in both classes, we can create it inside the Animal class and allow the Dog class to inherit it.

This avoids code duplication and improves code reusability.

How to Implement Inheritance?

In Java, we use the extends keyword to inherit one class from another.

class Animal {

    void eat() {
        System.out.println("Eating...");
    }

}

class Dog extends Animal {

    void bark() {
        System.out.println("Barking...");
    }

}

public class Main {

    public static void main(String[] args) {

        Dog dog = new Dog();

        dog.eat();
        dog.bark();

    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

Eating...
Barking...
Enter fullscreen mode Exit fullscreen mode

Here:

  • Animal is the parent class (superclass).
  • Dog is the child class (subclass).
  • extends is used to establish inheritance.

The Dog class can access the eat() method from the Animal class without creating it again.

Types of Inheritance in Java

  • Single Inheritance
  • Multilevel Inheritance
  • Hierarchical Inheritance
  • Hybrid Inheritance

1) Single Inheritance

One child class inherits from one parent class.

class Animal { }

class Dog extends Animal { }
Enter fullscreen mode Exit fullscreen mode

2) Multilevel Inheritance

A class inherits from another child class, forming a chain.

class Animal { }

class Dog extends Animal { }

class Puppy extends Dog { }
Enter fullscreen mode Exit fullscreen mode

Here, Puppy inherits from Dog, and indirectly from Animal.

3) Hierarchical Inheritance

Multiple child classes inherit from the same parent class.

class Animal { }

class Dog extends Animal { }

class Cat extends Animal { }
Enter fullscreen mode Exit fullscreen mode

Both Dog and Cat inherit from Animal.

4) Hybrid Inheritance

Hybrid inheritance is a combination of two or more types of inheritance.

Java doesn't support hybrid inheritance through classes alone because it doesn't support multiple inheritance through classes.

However, we can achieve hybrid inheritance using interfaces.

Example:

class Animal {

    void eat() {
        System.out.println("Eating...");
    }

}

interface Pet {

    void play();

}

class Dog extends Animal implements Pet {

    public void play() {
        System.out.println("Playing...");
    }

}

class Puppy extends Dog { }
Enter fullscreen mode Exit fullscreen mode

Here:

  • Dog extends Animal and implements Pet.
  • Puppy extends Dog.
  • This combines hierarchical/multiple inheritance concepts through classes and interfaces.

Note: Java doesn't support multiple inheritance through classes. A class cannot extend two classes at the same time. This avoids ambiguity when parent classes contain methods with the same signature.

However, Java supports multiple inheritance of type through interfaces.

Method Overloading in Java

Method overloading means creating multiple methods with the same name but different parameter lists.

We can change:

  • Number of parameters.
  • Type of parameters.
  • Order of parameter types.

Example:

class Calculator {

    int add(int a, int b) {
        return a + b;
    }

    int add(int a, int b, int c) {
        return a + b + c;
    }

    double add(double a, double b) {
        return a + b;
    }

}
Enter fullscreen mode Exit fullscreen mode

Here, all three methods have the same name but different parameters.

Compile-Time Polymorphism

Method overloading is also called compile-time polymorphism because the compiler determines which overloaded method to call based on the arguments.

Let's take another example from Java itself.

System.out.println("Java");
System.out.println(100);
System.out.println(10.5);
System.out.println(100L);
Enter fullscreen mode Exit fullscreen mode

All these statements use the same println() method name.

But how does Java print different data types?

The PrintStream class provides multiple overloaded versions of println() for different argument types, such as String, int, double, long and Object.

The compiler selects the appropriate overloaded method based on the argument type.

That's method overloading in action!

Method Overriding in Inheritance

Method overriding happens when a child class provides its own implementation of a method already available in the parent class.

class Animal {

    void sound() {
        System.out.println("Animal makes sound");
    }

}

class Dog extends Animal {

    @Override
    void sound() {
        System.out.println("Dog barks");
    }

}
Enter fullscreen mode Exit fullscreen mode

Here, the Dog class overrides the sound() method of the Animal class.

Runtime Polymorphism

Method overriding is also called runtime polymorphism because the method implementation is selected at runtime based on the actual object.

Example:

Animal animal = new Dog();

animal.sound();
Enter fullscreen mode Exit fullscreen mode

Output:

Dog barks
Enter fullscreen mode Exit fullscreen mode

Even though the reference type is Animal, the actual object is Dog.

So Java executes the overridden method from the Dog class.

Method Overloading vs Method Overriding

Method Overloading Method Overriding
Same method name, different parameters Same method signature
Usually happens within the same class Happens between parent and child classes
Compile-time polymorphism Runtime polymorphism
Inheritance is not required Inheritance is required

The super Keyword

The super keyword is used to refer to the immediate parent class.

It is commonly used to:

  • Access parent class variables.
  • Call parent class methods.
  • Call parent class constructors using super().

Example:

class Animal {

    void sound() {
        System.out.println("Animal Sound");
    }

}

class Dog extends Animal {

    void sound() {

        super.sound();

        System.out.println("Dog Barks");

    }

}
Enter fullscreen mode Exit fullscreen mode

Here, super.sound() calls the parent class method.

The Object Class in Java

This is an important concept in Java inheritance.

In Java, the Object class is the root superclass of all classes.

It belongs to the java.lang package.

If we create a class without extending any other class, Java implicitly makes it a subclass of Object.

For example:

class Student {

    String name = "John";

}
Enter fullscreen mode Exit fullscreen mode

Internally, it is equivalent to:

class Student extends Object {

    String name = "John";

}
Enter fullscreen mode Exit fullscreen mode

This means every Java class inherits methods from the Object class.

Some commonly used methods are:

  • toString()
  • equals()
  • hashCode()
  • getClass()

How Does toString() Work?

The toString() method belongs to the Object class.

Whenever we print an object using System.out.println(), Java calls its toString() method to get a String representation of that object.

Let's understand with an example.

1) Printing a String Object

String name = "Java";

System.out.println(name);
Enter fullscreen mode Exit fullscreen mode

Output:

Java
Enter fullscreen mode Exit fullscreen mode

Why does it print Java?

Because the String class overrides the toString() method inherited from the Object class.

Its implementation returns the actual string value.

2) Printing a Normal Object

class Student {

    String name = "John";

}

public class Main {

    public static void main(String[] args) {

        Student student = new Student();

        System.out.println(student);

    }
}
Enter fullscreen mode Exit fullscreen mode

Output (example):

Student@5acf9800
Enter fullscreen mode Exit fullscreen mode

Why does this happen?

The Student class hasn't overridden the toString() method.

So Java uses the inherited toString() implementation from the Object class.

The default implementation returns a String containing the class name, @, and the hexadecimal representation of the object's hash code.

3) Overriding toString()

We can override toString() to get a meaningful representation of our object.

class Student {

    String name = "John";

    @Override
    public String toString() {

        return name;

    }

}

public class Main {

    public static void main(String[] args) {

        Student student = new Student();

        System.out.println(student);

    }
}
Enter fullscreen mode Exit fullscreen mode

Output:

John
Enter fullscreen mode Exit fullscreen mode

Now, instead of printing the default object representation, Java prints the value returned by our overridden toString() method.

Important Points

  • Inheritance helps us reuse existing code.
  • We use the extends keyword for class inheritance.
  • Java supports single, multilevel and hierarchical inheritance through classes.
  • Hybrid inheritance can be achieved using classes and interfaces.
  • Java doesn't support multiple inheritance through classes.
  • The super keyword refers to the immediate parent class.
  • Method overloading is compile-time polymorphism.
  • Method overriding is runtime polymorphism.
  • The Object class is the root superclass of all Java classes.
  • Every Java class inherits methods from the Object class.
  • toString() returns a String representation of an object.
  • We can override toString() to provide our own object representation.

Top comments (0)