Java is an object-oriented programming language, which means it revolves around the concept of objects and classes. To manage and structure code effectively, Java follows four core principles of OOP: abstraction, polymorphism, encapsulation, and inheritance. In this blog, we’ll explore one of these key pillars — inheritance in Java language
Simply put, inheritance allows one class to reuse the code and behaviour of another. This helps make our code more modular, organised, and reusable.
But is that all there is to it? Can a principle as important as inheritance be summed up in just one line? Not quite!
Let’s take a deeper dive into this concept and understand how it works in Java.
Let’s start…
Technically speaking, Inheritance in Java is a mechanism of creating a new class or interface from an existing one. This new class or interface is allowed to inherit the properties and behaviour of the class or interface from which it has been created.
The class that has been created is called the subclass, while the class from which it has inherited the properties and behaviours is called the parent class. Now, these inherited properties can be used in several ways in our code. Some of them are –
According to Oracle docs, a subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not class members and therefore are not inherited by subclasses; however, a subclass can explicitly invoke its superclass’s constructor.
One thing to note is that inheritance is possible only when the two classes have an is-a relationship.
We can only use inheritance in a class by using the extends keyword, as this keyword helps us create a subclass with the properties and behaviours of its parent class.
Check out the code below.
public class Test {
public static void main(String[] args) {
Bicycle b = new Bicycle(50, 20);
MountainBike mb = new MountainBike(3, 100, 25);
System.out.println(mb.toString());
System.out.println();
System.out.println(b.toString());
}
}
// Base class
class Bicycle {
public int gear;
public int speed;
// Constructor of Bicycle
public Bicycle(int g, int s) {
gear = g;
speed = s;
}
// Method to apply brake
public void applyBrake(int decrement) {
speed -= decrement;
}
// Method to speed up
public void speedUp(int increment) {
speed += increment;
}
// toString() method to print info of Bicycle
public String toString() {
return "No of gears are " + gear + "\n"
+ "Speed of bicycle is " + speed;
}
}
// Derived class
class MountainBike extends Bicycle {
// Field specific to MountainBike
public int seatHeight;
// Constructor of MountainBike
public MountainBike(int g, int s, int startHeight) {
// Invoking base-class (Bicycle) constructor
super(g, s);
seatHeight = startHeight;
}
// Method to set seat height
public void setHeight(int newValue) {
seatHeight = newValue;
}
// Overriding toString() method
@Override
public String toString() {
return super.toString() + "\nSeat height is " + seatHeight;
}
}
In the above code, we have
In the main method, we have written ‘System.out.println(b.toString())’. This will automatically call the toString() method of the Bicycle object, and this will print the first two statements of our output.
This is because the method has the return keyword, which sends the value back to the caller (in this case, the object), and the System.out.println() method then prints that string to the console.
We already know that the purpose of the return keyword in a constructor is to exit the constructor early. But not to send anything back. But the purpose of the return keyword in a method is to exit a method early and to send back a value, and that’s what happened here.
So when we write the following statement
public String toString() {
return ("No of gears are " + gear + "\n" + "speed of bicycle is " + speed);
}
It will print the return statement with the values given as arguments at the time of object creation. Thus we get
No of gears are 50
speed of bicycle is 20
Similarly, when we write ‘System.out.println(mb.toString());’, it will call the toString() method of the MountainBike object, which will trigger the return statement of the method and print the text written inside the return statement.
However, there is a catch: in the return statement of the MountainBike object, we have used the super keyword. The super keyword is also a keyword in Java, just like the return keyword. It is used to refer to the parent class from the child class. In our case, it refers to the Bicycle class from the MountainBike class.
Usage of the Super Keyword in Java
Thus, the statement –
return (super.toString() + "\nseat height is " + seatHeight);
Thus, the output
No of gears are 3
speed of bicycle is 100
seat height is 25
There are 5 types of inheritance in OOP. They are –
Let’s discuss them one at a time –
A type of inheritance in which the child class inherits the properties of the parent class is known as single inheritance. It is the basic form of inheritance, as only one subclass is inheriting from the parent class.
We know that for inheritance to occur, the classes must share an is-a relationship. For example, we can say that a Dog is a type of Animal. Since this is an is-a relationship, the Dog class can inherit properties and behaviours from the Animal class. This is a perfect example of a single inheritance, where one subclass inherits from a single superclass.
Let’s see this in action.
public class Main {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
d.bark();
}
}
class Animal {
void eat() {
System.out.println("This animal eats food.");
}
}
class Dog extends Animal {
void bark() {
System.out.println("The dog barks.");
}
}
The animal eats food.
The dog barks.
As the name suggests, multilevel inheritance is a type of inheritance that spans across multiple levels — it’s essentially a chain of single inheritances.
To understand this better, let’s break it down with a simple example involving three classes: A, B, and C.
This step-by-step chain — A → B → C — forms a multilevel inheritance, where each level builds upon the one before it.
Here’s a simple diagram to illustrate:
Multilevel inheritance helps us avoid repetitive code and lets us build more specific classes based on general ones, making our programs easier to manage and scale.
We will be using the same code that we have used above – the one with the Bicycle class as the parent class and MountainBike class as the child class, to understand multilevel Inheritance by adding a parent class – Vehicle to it, and also with some added features like a derived method, etc.
So the inheritance tree will become
public class Test {
public static void main(String[] args) {
// Creating an object of MountainBike
MountainBike mb = new MountainBike(20, 5, true, 30);
// Displaying details
System.out.println("---- Vehicle Details ----");
mb.showDetails(); // From Vehicle class
System.out.println("\n---- Bicycle Details ----");
mb.showBicycleDetails(); // From Bicycle class
System.out.println("\n---- MountainBike Details ----");
mb.showMountainBikeDetails(); // From MountainBike class
// Applying methods from different levels of inheritance
mb.applyBrake(5); // From Vehicle class
mb.adjustSeatHeight(35); // From MountainBike class
}
}
class Vehicle {
// Common fields for all vehicles
public int speed;
public int gear;
// Constructor to initialise speed and gear
public Vehicle(int spd, int gr) {
speed = spd;
gear = gr;
}
// Method to display vehicle details
public void showDetails() {
System.out.println("Speed: " + speed + " km/h");
System.out.println("Gear: " + gear);
}
// Method to apply brakes
public void applyBrake(int decrement) {
speed -= decrement;
System.out.println("Speed after applying brake: " + speed + " km/h");
}
}
// Intermediate class (child of Vehicle)
class Bicycle extends Vehicle {
// Additional field specific to Bicycle
public boolean hasBell;
// Constructor to initialise Bicycle properties
public Bicycle(int spd, int gr, boolean bell) {
super(spd, gr); // Calls the constructor of Vehicle
hasBell = bell;
}
// Method to display Bicycle-specific details
public void showBicycleDetails() {
System.out.println("Has bell: " + (hasBell ? "Yes" : "No"));
}
}
// Most specialised class (child of Bicycle)
class MountainBike extends Bicycle {
// Additional field specific to MountainBike
public int seatHeight;
// Constructor to initialise MountainBike properties
public MountainBike(int spd, int gr, boolean bell, int height) {
super(spd, gr, bell); // Calls the constructor of Bicycle
seatHeight = height;
}
// Method to adjust seat height
public void adjustSeatHeight(int newHeight) {
seatHeight = newHeight;
System.out.println("Seat height adjusted to: " + seatHeight + " cm");
}
// Method to display MountainBike-specific details
public void showMountainBikeDetails() {
System.out.println("Seat Height: " + seatHeight + " cm");
}
}
In the code above, we demonstrate multilevel inheritance using three classes: Vehicle, Bicycle, and MountainBike.
When the MountainBike object is created, constructors are called from the top of the hierarchy (Vehicle) down to MountainBike, thanks to the super keyword. This ensures that all inherited properties are properly initialized.
The MountainBike class can use methods and properties from both its parent (Bicycle) and grandparent (Vehicle), showcasing how each level of inheritance builds upon the previous one (multilevel inheritance) and also why it can and has access to every property like gear, speed, bell, etc
From this, we can easily say that Multilevel inheritance consists of multiple single inheritance steps chained together, where each class inherits from one parent. Java supports this kind of inheritance, allowing a class to inherit from a class that already inherited from another..
This is very different from the ones we have understood above. Let’s first try to understand it with the help of an example before going to the actual theory.
Imagine a company with different types of employees. There’s a basic Employee class that has common details like name, employee ID, and a method called work(). These are things that every employee in the company has or does.
Now, let’s say there are three types of employees: Writer, Developer, and Designer. We can create three new classes for them. Since they are all employees, they can inherit from the Employee class. That means they automatically get the name, employeeId, and work() method — without writing that code again.
This idea, where one parent class (Employee) is shared by many child classes (Writer, Developer, Designer), is called hierarchical inheritance in Java.
public class CompanyDemo {
public static void main(String[] args) {
Writer writer = new Writer();
writer.name = "Alice";
writer.id = 101;
writer.work(); // Inherited from Employee
writer.manageTeam(); // Specific to Writer
System.out.println();
Developer developer = new Developer();
developer.name = "Bob";
developer.id = 102;
developer.work(); // Inherited from Employee
developer.writeCode(); // Specific to Developer
System.out.println();
Designer designer = new Designer();
designer.name = "Charlie";
designer.id = 103;
designer.work(); // Inherited from Employee
designer.design(); // Specific to Designer
}
}
class Employee {
String name;
int id;
void work() {
System.out.println(name + " is working.");
}
}
class Writer extends Employee {
void manageTeam() {
System.out.println(name + " is managing the team.");
}
}
class Developer extends Employee {
void writeCode() {
System.out.println(name + " is writing code.");
}
}
class Designer extends Employee {
void design() {
System.out.println(name + " is designing.");
}
}
Alice is working.
Alice is managing the team.
Bob is working.
Bob is writing code.
Charlie is working.
Charlie is designing.
In this example, each specific type of employee does something extra besides just “working.” But they all still share the base behaviour from the Employee class. That’s the beauty of hierarchical inheritance; it helps us avoid repeating code and keeps things organised.
As the name suggests, a type of inheritance in which a child class has inherited properties of two parent classes, just like a child in a family inherits traits and qualities from both of their parents.
But Java doesn’t support multiple inheritance in the normal way, as it leads to a problem that we know as the diamond problem, causing ambiguity, so to prevent it from happening, interfaces are used.
Let’s understand this with the help of a real-world analogy. Imagine we are learning cooking from our mom and learning painting from our dad. Our mom teaches us how to cook delicious meals, while our dad teaches us how to paint beautiful pictures.
In this example, we are learning two different skills from two different people. This is similar to the idea of multiple inheritance, where one class tries to learn or inherit features from more than one parent class.
Let’s see that in action
// Mom class
class Mom {
public void cook() {
System.out.println("Cooking delicious meals like Mom.");
}
}
// Dad class
class Dad {
public void paint() {
System.out.println("Painting beautiful pictures like Dad.");
}
}
// Trying to inherit from both Mom and Dad — NOT allowed
class Child extends Mom, Dad {
public void showSkills() {
cook(); // From Mom
paint(); // From Dad
}
}
// Main class
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.showSkills();
}
}
Now, in Java, this kind of learning from two sources (or classes) at the same time is not allowed with classes. That means we cannot have a class that extends two classes at once. This can cause ambiguity.
For example, if both mom and dad teach us different ways to boil an egg, we can get confused. Similarly, the computer wouldn’t know which version to follow. This is called the diamond problem, and Java avoids it by not allowing multiple inheritance with classes.
However, Java 8 supports default methods, where interfaces can provide a default implementation of methods, as a class can implement more than one interface. Now let’s make the necessary changes in the above code, and try to compile it again.
// Mom interface
interface Mom {
void cook();
}
// Dad interface
interface Dad {
void paint();
}
// Child class implementing both interfaces
class Child implements Mom, Dad {
public void cook() {
System.out.println("Cooking delicious meals like Mom.");
}
public void paint() {
System.out.println("Painting beautiful pictures like Dad.");
}
public void showSkills() {
cook(); // From Mom
paint(); // From Dad
}
}
// Main class
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.showSkills();
}
}
Output
Cooking delicious meals like Mom.
Painting beautiful pictures like Dad.
And we have our output. What we did was just replace the Mom class and Dad class with the Mom interface and Dad interface, and replace the extends keyword with the implements keyword, as a class cannot extend two classes at the same time, but can implement them.
Imagine a situation where two interfaces, Interface A and Interface B, both have a default method with the same name and parameters (same method signature), and a class called MyClass wants to implement both Interface A and Interface B.
interface InterfaceA {
default void show() {
System.out.println("InterfaceA's default show method");
}
}
interface InterfaceB {
default void show() {
System.out.println("InterfaceB's default show method");
}
}
class MyClass implements InterfaceA, InterfaceB {
// No method override here — this will cause a compile-time error
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show(); // ❌ Compile-time error: Duplicate default methods
}
}
As shown above, this causes a conflict because Java doesn’t know which version of the method to use, the one from InterfaceA or the one from InterfaceB. To solve this, the class must handle the conflict in one of these two ways.
By just providing our version of the methods, the compiler will override the original method(s).
interface InterfaceA {
default void show() {
System.out.println("InterfaceA's default show method");
}
}
interface InterfaceB {
default void show() {
System.out.println("InterfaceB's default show method");
}
}
class MyClass implements InterfaceA, InterfaceB {
@Override
public void show() {
System.out.println("MyClass's version of the show method");
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show();
}
}
MyClass’s version of the show method
We can use the super keyword to prioritise one method over the other. Let’s say we have prioritised Interface A over Interface B. Then the code becomes
interface InterfaceA {
default void show() {
System.out.println("InterfaceA's default show method");
}
}
interface InterfaceB {
default void show() {
System.out.println("InterfaceB's default show method");
}
}
class MyClass implements InterfaceA, InterfaceB {
@Override
public void show() {
InterfaceA.super.show(); // Calls InterfaceA's default method
}
}
public class Main {
public static void main(String[] args) {
MyClass obj = new MyClass();
obj.show();
}
}
InterfaceA’s default show method
But what if we say that there is a way to achieve multiple inheritance in classes? It’s called the composition method (the has-a relationship)
It is a different approach to inheritance than the usual is-a relationship. In an is-a relationship, we say (taking the Mom, Dad and child example), the Child class extends the Mom class. But here we can say that the Child class has a Mom class and a Dad class.
Let’s see this approach in action as well, and then we will understand the changes we have made.
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.showSkills();
}
}
// Mom class
class Mom {
public void cook() {
System.out.println("Cooking like Mom.");
}
}
// Dad class
class Dad {
public void paint() {
System.out.println("Painting like Dad.");
}
}
// Child class has-a Mom and Dad
class Child {
private Mom mom;
private Dad dad;
public Child() {
mom = new Mom();
dad = new Dad();
}
public void showSkills() {
mom.cook(); // Delegating to Mom
dad.paint(); // Delegating to Dad
}
}
Cooking like Mom.
Painting like Dad.
In the above code, instead of inheriting from multiple classes using extends, the child class holds references to instances of both the Mom and Dad classes. Thus, the Child class contains two instance variables: one of type Mom and the other of type Dad.
By doing this, the Child class composes the desired behaviour from both Mom and Dad, rather than inheriting it. This allows the class to reuse functionality from multiple sources while avoiding the complications of multiple inheritance.
Other than the instance variable, the Child class also has a constructor and a method. Inside its constructor, two new objects have been created and are linked with their private fields. This is the ‘has-a’ relationship that we were talking about.
In short, composition (has-a) means a class uses other classes via fields.
Inside the showSkills() method, the Child object delegates (asking another object to do a task for us) the cooking and painting actions to its Mom and Dad objects by calling their cook() and paint() methods. This is known as method delegation, a process in which one object calls the methods of another to get things done.
The big advantage here is that we can easily change the logic of both cooking and painting, as they have been separately written inside the Mom and Dad classes, respectively. This makes the code modular, flexible, and easier to maintain.
It is a little different from the rest of the Inheritances, though the name itself makes it pretty much clear. The combination of two or more different types of inheritance discussed above is called a Hybrid Inheritance. Like Single and Hierarchical inheritance, multiple and multilevel inheritance, etc.
Let’s understand a simple hybrid inheritance (an inheritance without multiple inheritance) with the help of an example of a family with four members: grandfather, father, uncle, and son.
In this hierarchy:
Since the Son class inherits from the Father class, which in turn inherits from the Grandfather class, this also forms a multilevel inheritance structure.
Let’s understand the above scenario in action.
public class FamilyTree {
public static void main(String[] args) {
Son son = new Son();
// Inherited from Grandfather
son.showGrandfather();
// Inherited from Father
son.showFather();
// Own method
son.showSon();
// Line break
System.out.print("\n");
// Demonstrating Uncle separately
Uncle uncle = new Uncle();
uncle.showGrandfather(); // Inherited from Grandfather
uncle.showUncle(); // Uncle's own method
}
}
// Base class
class Grandfather {
void showGrandfather() {
System.out.println("I am the Grandfather.");
}
}
// Father class inherits from Grandfather
class Father extends Grandfather {
void showFather() {
System.out.println("I am the Father.");
}
}
// Uncle class also inherits from Grandfather (hierarchical inheritance)
class Uncle extends Grandfather {
void showUncle() {
System.out.println("I am the Uncle.");
}
}
// Son class inherits from Father (Single Inheritance)
class Son extends Father {
void showSon() {
System.out.println("I am the Son.");
}
}
I am the Grandfather.
I am the Father.
I am the Son.
I am the Grandfather.
I am the Uncle.
The code contains four classes: Grandfather, Father, Uncle, and Son. Each class has its own unique method. Through inheritance, child classes can access the methods of their parent classes. For instance, the Father inherits from the Grandfather, and the Son inherits from the Father. This means the Son has access to methods from both Father and Grandfather.
In the main method, an object of the Son class is created. Since the Son inherits all methods from the father and grandparent classes, there’s no need to create separate objects for Father or Grandfather. All those methods can be accessed through the Son object.
The Uncle class also inherits from the Grandfather, but no class inherits from Uncle. So, to access its behaviour, a separate object of Uncle is created.
From our above discussion, we know that Java doesn’t support multiple inheritance through classes. We have to work with the interface to form a hybrid inheritance with multiple inheritance.
So, we can say that hybrid inheritance involving multiple inheritance is also not possible with classes. In Java, we can achieve hybrid inheritance only through Interfaces if we want to involve multiple inheritance to implement Hybrid inheritance.
Since Java supports single, multilevel, and hierarchical inheritance using classes—but only supports multiple inheritance through interfaces—hybrid inheritance involving multiple inheritance can only be achieved through interfaces.
This leads to an important consideration: how can we implement hybrid inheritance in Java that combines class-based inheritance (like single inheritance) with interface-based inheritance (multiple inheritance)?
The solution is to use a class to extend another class (for single inheritance) and simultaneously implement multiple interfaces (for multiple inheritance). As this is something new and slightly advanced (we are using the extends keyword and the implements keyword at the same time), let’s understand it through a simple Java code.
public class FamilyTree {
public static void main(String[] args) {
Son son = new Son();
// Inherited from Grandfather
son.showGrandfather();
// Inherited from Father
son.showFather();
// Inherited from Uncle interface
son.showUncle();
// Son's own method
son.showSon();
// Line break
System.out.print("\n");
// Demonstrating actual Uncle implementation
UncleImpl uncle = new UncleImpl();
uncle.showGrandfather(); // Inherited from Grandfather
uncle.showUncle(); // Inherited from Uncle interface
uncle.old();
}
}
// Base class
class Grandfather {
void showGrandfather() {
System.out.println("I am the Grandfather.");
}
}
// Father class inherits from Grandfather (single inheritance)
class Father extends Grandfather {
void showFather() {
System.out.println("I am the Father.");
}
}
// Declaring Uncle as an interface (not a class)
interface Uncle {
default void showUncle() {
System.out.println("I am the Uncle.");
}
}
// Son inherits from Father and implements Uncle (multiple inheritance via interface)
class Son extends Father implements Uncle {
void showSon() {
System.out.println("I am the Son.");
}
}
// Concrete class to represent Uncle independently
class UncleImpl extends Grandfather implements Uncle {
void old() {
System.out.print("Hi! I am UncleImpl");
}
}
I am the Grandfather.
I am the Father.
I am the Uncle.
I am the Son.
I am the Grandfather.
I am the Uncle.
Hi! I am UncleImpl
The code is a bit lengthy and full of information; let’s try to understand it through a diagram also.
Before understanding the above code, let’s first understand the relationships between different interfaces and classes, as it might make things easier through the above image.
It is quite clear that to create a hybrid inheritance using multiple inheritance, we have to either create a new interface or convert a class into an interface. And we have gone with the latter in the above code, we have converted the class Uncle into an interface
Now, the Son class, which in the above example only extends the Father class, implements the Uncle interface as well, giving us our multiple inheritance scenario.
And the UncleImpl class that has been specifically created for the Uncle interface. This class extends the Grandfather class and implements the Uncle interface so that we can print the methods and attributes of the Uncle Interface as well.
Inheritance is one of the principles of Java OOPS in which child classes are dependent on their parent class for properties and methods. This may sometimes lead to bugs and errors, as any changes to the parent class will be reflected in the child class. Like
In this blog, we discussed inheritance and its types, along with why it’s a key principle of Object-Oriented Programming (OOP). Simply put, inheritance is the process by which one class (the subclass) inherits properties and methods from another class (the superclass) and can extend or use them as needed.
Inheritance is a powerful feature in Java that promotes code reuse, reduces redundancy, and helps create a clear structure between related classes. Java mainly supports five types of inheritance: Single, Multilevel, Multiple, Hierarchical, and Hybrid.
Single, Multilevel, and Hierarchical inheritance are class-based. Multiple inheritance in Java is achieved using interfaces, since Java does not support multiple inheritance with classes. Hybrid inheritance is a combination of these types and can involve both classes and interfaces.
Understanding these types of inheritance will help us write more efficient and organised Java programs.
Ans. It is used when we want a class to inherit properties of another class, forming an is-a relationship between them.
Ans. The key difference between Multiple and Multilevel Inheritance is that Multiple Inheritance is when a class inherits from many base classes, while Multilevel Inheritance is when a class inherits from a derived class, making that derived class a base class for a new class.
Ans. The reason that Inheritance is used is that it lets us use the properties and methods of the base class without any restriction as long as it follows the is-a relationship, we can modify, keep them as it is, add something new or leave something that is not required; it’s completely up to us.
Ans. No, private members of the parent class are not accessible directly in the child class. However, they can be accessed indirectly through public or protected methods of the parent class.
Ans. No, a class declared as final cannot be inherited. The final keyword prevents any subclass from extending the class.
In inheritance, constructors are used to initialize objects of a class. When a derived (child) class object is created, the constructor of the base (parent) class is called first to initialize the inherited attributes, followed by the constructor of the derived class to initialize its own attributes. This ensures proper initialization of both base and derived class members.
Well, inheritance is considered to be a good design choice because of two reasons –
In this blog, we will not only understand the concepts of OOPS in Java in…
Object-Oriented Programming System (OOPS) is a programming paradigm built around the concept of objects —…
Abstraction in Java is one of the four pillars of OOPs which is used to…
In this blog, we will learn How to Detect a Click Outside of a React…
learn How to Use Hooks to Create Infinite Scrolling in React by making a custom…
React Movie App or Movie App in React is a fun project that every React…