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(); } } Output:
Eating... Barking... Here:
-
Animalis the parent class (superclass). -
Dogis the child class (subclass). -
extendsis 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 { } 2) Multilevel Inheritance
A class inherits from another child class, forming a chain.
class Animal { } class Dog extends Animal { } class Puppy extends Dog { } 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 { } 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 { } Here:
-
DogextendsAnimaland implementsPet. -
PuppyextendsDog. - 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; } } 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); 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"); } } 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(); Output:
Dog barks 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"); } } 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"; } Internally, it is equivalent to:
class Student extends Object { String name = "John"; } 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); Output:
Java 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); } } Output (example):
Student@5acf9800 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); } } Output:
John 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
extendskeyword 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
superkeyword refers to the immediate parent class. - Method overloading is compile-time polymorphism.
- Method overriding is runtime polymorphism.
- The
Objectclass is the root superclass of all Java classes. - Every Java class inherits methods from the
Objectclass. -
toString()returns a String representation of an object. - We can override
toString()to provide our own object representation.
