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…
Index
What is inheritance in Java?
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 –
- By overriding them
- By reusing them, which will save both time and effort
- By making something entirely new
- By using them partly
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.
How to Use Inheritance in Java?
We can only use inheritance in a class using the extends keyword, as this keyword helps us in creating 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;
}
}
Output
In the above code, we have
- A Bicycle class with two public fields – speed and gear
- Two parameterised constructors for the Bicycle class and the MountainBike class.
- Two instance methods to modify the bicycle speed – applyBreak() and speedUp().
- A special method – toString(), which returns a string representation of an object when printed.
- A subclass: MountainBike, which extends the Bicycle class.
- A derived method – setHeight()
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.
- The number of gears
- The speed of the bicycle
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.
But here is the 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
- It can be used to refer to an immediate parent class instance variable.
- It can be used to invoke the immediate parent class method.
- It can be used to invoke the immediate parent class constructor.
Thus, the statement –
return (super.toString() + "\nseat height is " + seatHeight);
- First calls the toString() method of the parent (Bicycle) class and prints its return statement.
- Then prints the value of the derived method (setHeight()).
Thus, the output
No of gears are 3
speed of bicycle is 100
seat height is 25
Types of Inheritance in Java
There are 5 types of inheritance in OOP. They are –
- Single Inheritance
- Multilevel Inheritance
- Hierarchical Inheritance
- Multiple inheritance
- Hybrid Inheritance
Let’s discuss them one at a time –
Single inheritance
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.
Code Example
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.");
}
}
Output
Multilevel Inheritance
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.
- Class A is the base class.
- Class B inherits from Class A. This is a basic example of single inheritance, where one class (B) gets the properties and methods of another (A).
- Now, class C inherits from class B. Since class B is already inherited from class A, class C now indirectly inherits from both A and B.
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.
Understanding multilevel Inheritance with a code
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");
}
}
Output
In the code above, we demonstrate multilevel inheritance using three classes: Vehicle, Bicycle, and MountainBike.
- The Vehicle class serves as the base class, containing common properties like speed and gear, along with methods – showDetails and applyBrakes.
- The Bicycle class extends Vehicle and adds a new property, hasBell, along with a method to display it.
- The MountainBike class further extends Bicycle, introducing an additional property, seatHeight, and methods related to seat adjustment.
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..
Hierarchical Inheritance
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.
Understanding Hierarchical Inheritance through code
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.");
}
}
Output
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.
Multiple Inheritance in Java
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();
}
}
Output
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 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();
}
}
Output
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.
Why did this work?
- Interfaces Only Declare Methods: Interfaces declare methods but do not provide implementations. So, there is no conflict or ambiguity about which implementation to use.
- Single Implementation in Subclass: The implementing class provides its implementation of the methods, resolving any potential conflicts.
Edge Case
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
}
}
Output
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 overriding
- By Prioritising one over the other
By Overriding
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();
}
}
Output
Prioritising one over the other
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();
}
}
Output
Multiple inheritance in classes using Composition
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
}
}
Output
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.
Hybrid Inheritance
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.
Hybrid Inheritance without Multiple Inheritance
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:
- The Grandfather class is the base class.
- The Father and Uncle classes are subclasses of the Grandfather class — this represents hierarchical inheritance.
- The Son class is a subclass of the Father class — this represents single inheritance.
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.");
}
}
Output
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.
Hybrid Inheritance using Multiple Inheritance
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");
}
}
Output
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.
- Grandfather Class – Base class
- Father Class extends the Grandfather class -> Single inheritance
- Son Class extends the Father Class and implements the Uncle Interface -> multiple inheritance
- Uncle Interface
- UncleImpl Class extends Grandfather Class and implements Uncle Interface -> multiple inheritance
- Son class extends Father class, which has already extended the Grandfather Class -> multilevel class
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.
Advantages of inheritance in Java
- Code reusability– we can reuse the same piece of code as many times as possible, either as it is or by modifying it to fit the code requirements.
- Improves Code Modularity – Modularity is a common term in software engineering that tells us how easy and well-structured the code is, and inheritance helps organise and reuse code in a clean and manageable way.
- Easier maintenance – Well-structured code is a must, making an app in any language, as it makes it that much easier to debug it later on, and with inheritance, finding and fixing bugs is easy, as it gives our code a proper structure.
- Faster Development – When the code works well on the DRY principle, it is modular and can be easily maintained, the development process becomes that much faster and with fewer errors. This also makes it easier to read.
- Polymorphism – inheritance allows the subclasses to provide their own implementation of the method defined in the parent class, which will only be called dynamically at runtime.
- Encapsulation – Not everything is inherited by a single child class, as the parent class might protect its data by using access modifiers. Thus, using the getters and setters methods of encapsulation, we can alter the data of the parent class while keeping everything else safe and modular.
Disadvantages of Inheritance in Java
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
- Tight Coupling – too much dependence of child class on parent class
- Fragile base class – if there is any change in the parent class, it will be reflected in the child class, making it unstable.
- Tracing overridden behaviours – the same is true of child classes, too much overriding may make it difficult to trace the parent class from it.
- Unused functions – child classes will inherit all the methods and properties of their parent class, even if there is no use, which will increase the overall space taken.
- Difficulty in debugging – Inheritance can sometimes make things complex, which in turn makes debugging and maintaining the code harder.
Important Points
- Private members of a parent class are not directly accessible in the child class.
- A child class can use public or protected methods of the parent class.
- A constructor is not inherited, but the child class can call the parent’s constructor using super().
- If the parent doesn’t have a no-argument constructor, the child must explicitly call a constructor using super(…).
- The instance of keyword is used to check if an object is an instance of a specific class or its parent.
- Interfaces allow a class to inherit behaviour from multiple sources and help simulate multiple inheritance.
Conclusion
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.
Frequently Asked Questions
Q1. What is the use of the ‘extends’ keyword?
Ans. It is used when we want a class to inherit properties of another class, forming an is-a relationship between them.
Q2. What is the difference between multiple and multilevel Inheritance in Java?
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.
Q3. Why is Inheritance used?
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.
Q4. Can a child class inherit private members of the parent class?
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.
Q5. Can a final class be inherited in Java?
Ans. No, a class declared as final cannot be inherited. The final keyword prevents any subclass from extending the class.
Q6. What is the role of constructors in inheritance?
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.