Inheritance in Java
Introduction
Inheritance is one of the four fundamental pillars of Object-Oriented Programming (OOP), alongside Encapsulation, Polymorphism, and Abstraction. It is the mechanism by which one class is allowed to inherit the features (fields and methods) of another class.
In Java, inheritance allows you to create new classes that reuse, extend, and modify the behavior defined in other classes. The class whose properties are inherited is known as the Superclass (or parent/base class), and the class that inherits those properties is known as the Subclass (or child/derived class).
Core Concepts
The "IS-A" Relationship
Inheritance represents an IS-A relationship. For example, a Car IS-A Vehicle, and a Manager IS-A Employee. If a relationship cannot be described as "IS-A", you should likely use Composition (HAS-A relationship) instead of inheritance.
The extends Keyword
In Java, inheritance is implemented using the extends keyword. A subclass can only extend one superclass.
class Vehicle {
protected String brand = "Ford";
public void honk() {
System.out.println("Tuut, tuut!");
}
}
class Car extends Vehicle {
private String modelName = "Mustang";
public static void main(String[] args) {
Car myCar = new Car();
myCar.honk(); // Inherited method
System.out.println(myCar.brand + " " + myCar.modelName);
}
}
Types of Inheritance in Java
Java supports the following types of inheritance for classes:
- Single Inheritance: A subclass extends a single superclass.
- Multilevel Inheritance: A subclass extends a superclass, and then another class extends this subclass (e.g.,
C extends B, andB extends A). - Hierarchical Inheritance: Multiple subclasses extend the same superclass (e.g.,
B extends AandC extends A).
Why No Multiple Inheritance for Classes?
Java does not support multiple inheritance for classes (where a class extends more than one class). This design decision prevents the Diamond Problem—ambiguity that arises if two parent classes define the same method and the compiler cannot decide which one to inherit. (Note: Multiple inheritance is supported via Interfaces).
The super Keyword
The super keyword in Java is a reference variable used to refer to immediate parent class objects.
1. Invoking Parent Class Constructors
When a subclass object is created, the parent class constructor is implicitly called first. You can use super() to explicitly call a specific constructor of the parent class. It must be the first statement in the subclass constructor.
class Animal {
Animal(String name) {
System.out.println("Animal is created: " + name);
}
}
class Dog extends Animal {
Dog() {
super("Buddy"); // Calls the parameterized constructor of Animal
System.out.println("Dog is created");
}
}
2. Accessing Parent Class Methods and Fields
You can use super to invoke parent class methods that have been overridden in the subclass, or to access parent class fields if they are hidden by subclass fields.
class Parent {
void display() { System.out.println("Parent Display"); }
}
class Child extends Parent {
@Override
void display() {
super.display(); // Calls Parent's display
System.out.println("Child Display");
}
}
Method Overriding
Method Overriding allows a subclass to provide a specific implementation of a method that is already provided by its superclass.
Rules for Method Overriding:
- The method must have the same name as in the parent class.
- The method must have the same parameters (signature).
- The return type must be the same or a subtype (Covariant Return Type).
- The access modifier cannot be more restrictive than the overridden method.
- Always use the
@Overrideannotation to let the compiler verify that you are actually overriding a method.
The Object Class
In Java, every class implicitly inherits from the java.lang.Object class. If a class does not explicitly extend another class, it automatically extends Object. This means methods like toString(), equals(), hashCode(), and getClass() are available to all Java objects.
Best Practices and Pitfalls
- Favor Composition Over Inheritance: Deep inheritance hierarchies become brittle and hard to maintain. Often, composing objects together (HAS-A) provides more flexibility than inheriting them.
- Use
finalWhere Appropriate: If a class should not be extended, mark it asfinal(e.g.,java.lang.String). If a method should not be overridden, mark it asfinal. - Liskov Substitution Principle (LSP): A subclass should be substitutable for its superclass without breaking the program's correctness. If overriding a method breaks the expected behavior, your inheritance hierarchy is flawed.
Summary
Inheritance is a powerful tool for code reuse and establishing polymorphic behavior. By understanding how to properly use extends, super, and method overriding while avoiding unnecessarily deep hierarchies, you can design clean and robust Java applications.