What is inheritance in Java?
Inheritance is a fundamental concept in object-oriented programming (OOP) that allows a class to inherit properties and methods from another class. In Java, inheritance enables code reusability and establishes relationships between classes.
Table of Contents
The Interview Question
"What is inheritance in Java?"
Short Answer
Inheritance is one of the core principles of Object-Oriented Programming that enables a class (child/subclass) to inherit properties and behaviors from another class (parent/superclass). In Java, we use the extends keyword to implement inheritance.
This mechanism promotes code reuse, establishes a clear "is-a" relationship between classes, and allows for polymorphic behavior. Java supports single inheritance for classes (a class can extend only one class) but allows multiple inheritance through interfaces.
Detailed Explanation
Inheritance is one of those foundational OOP concepts you simply can't avoid in Java development. I've found it to be one of the most powerful tools for designing clean, maintainable code structures, but it requires a solid understanding to use effectively.
At its core, inheritance creates a parent-child relationship between classes where a child class (subclass) acquires attributes and behaviors from a parent class (superclass). This relationship is often described as an "is-a" relationship: a Car is a Vehicle, a Savings Account is an Account, and so on.
In Java, we use the extends keyword to establish this relationship:
class Vehicle {
// Vehicle properties and methods
}
class Car extends Vehicle {
// Car inherits all accessible properties and methods from Vehicle
// Plus can have its own Car-specific properties and methods
}
When we say Car "inherits" from Vehicle, it means Car automatically has access to Vehicle's non-private fields and methods without having to redefine them. This is a major benefit of inheritance – code reuse!
Types of Inheritance in Java
Java supports several forms of inheritance:
-
Single Inheritance: A class inherits from only one superclass. This is the standard in Java.
class Dog extends Animal { } -
Multilevel Inheritance: A class inherits from a class which itself inherits from another class.
class Animal { } → class Mammal extends Animal { } → class Dog extends Mammal { } -
Hierarchical Inheritance: Multiple classes inherit from a single superclass.
class Animal { } → class Dog extends Animal { } and class Cat extends Animal { }
Note
Java does not support multiple inheritance through classes (where a class directly inherits from more than one class) due to the "diamond problem" which can create ambiguity. However, Java does support a form of multiple inheritance through interfaces.
What Gets Inherited
Not everything from a parent class is inherited. Here's what a subclass inherits:
- Inherited: All
publicandprotectedmembers (fields, methods, nested classes) - Inherited:
default(package-private) members if the subclass is in the same package - Not Inherited:
privatemembers are never inherited - Not Inherited: Constructors are not inherited, but the parent's constructor is implicitly called
I've seen many developers get confused about what's actually inherited, especially regarding constructors. While constructors aren't inherited, the parent class constructor is always called when instantiating a child class - either implicitly or explicitly using super().
Key Benefits of Inheritance
- Code Reusability: Reuse fields and methods of the existing class
- Method Overriding: Achieve runtime polymorphism by providing specific implementations in subclasses
- Extensibility: Extend the functionality of existing classes
- Data Hiding: Protect data through encapsulation when combined with access modifiers
- Hierarchical Classification: Create clean, hierarchical relationships between concepts in your domain
Code Example
Let's explore a real-world example of inheritance in Java that demonstrates these concepts in action:
// Base class (Superclass)
class Employee {
private String name;
protected double baseSalary;
// Constructor
public Employee(String name, double baseSalary) {
this.name = name;
this.baseSalary = baseSalary;
}
// Methods
public String getName() {
return name;
}
public double calculateSalary() {
return baseSalary;
}
public void displayInfo() {
System.out.println("Employee: " + name);
System.out.println("Base Salary: $" + baseSalary);
}
}
// Derived class (Subclass)
class Manager extends Employee {
private double bonus;
// Constructor calls parent constructor using super()
public Manager(String name, double baseSalary, double bonus) {
super(name, baseSalary); // Call to parent constructor
this.bonus = bonus;
}
// Method overriding - providing a specialized implementation
@Override
public double calculateSalary() {
// Using protected parent field (baseSalary)
return baseSalary + bonus;
}
// Method overriding with extension of parent behavior
@Override
public void displayInfo() {
super.displayInfo(); // Call parent method
System.out.println("Bonus: $" + bonus);
System.out.println("Total Salary: $" + calculateSalary());
}
// Manager-specific method not in the parent class
public void manageTeam() {
System.out.println(getName() + " is managing the team.");
}
}
// Demonstration
public class InheritanceDemo {
public static void main(String[] args) {
// Create objects
Employee employee = new Employee("John Doe", 50000);
Manager manager = new Manager("Jane Smith", 70000, 10000);
// Use objects
System.out.println("===== Employee Information =====");
employee.displayInfo();
System.out.println("\n===== Manager Information =====");
manager.displayInfo();
manager.manageTeam();
// Demonstrate polymorphism
System.out.println("\n===== Polymorphism Demonstration =====");
Employee polymorphicEmployee = new Manager("Alice Johnson", 65000, 8000);
polymorphicEmployee.displayInfo(); // Calls Manager's version
// This won't compile because the reference type is Employee
// polymorphicEmployee.manageTeam();
}
}
Let's break down this example to understand inheritance in action:
- Class Hierarchy: We have an
Employeesuperclass and aManagersubclass that extends it. - Access to Parent Members: Manager can access the protected
baseSalaryfield and the publicgetName()method from Employee. - Constructor Chaining: Manager's constructor calls Employee's constructor using
super(). - Method Overriding: Manager overrides
calculateSalary()anddisplayInfo()to provide specialized behavior. - Extending Behavior: In
displayInfo(), Manager calls the parent implementation usingsuper.displayInfo()and then adds its own functionality. - Polymorphism: We can refer to a Manager object using an Employee reference (
polymorphicEmployee), demonstrating runtime polymorphism.
This example also illustrates a key limitation: when using a parent class reference to a child class object, you can only access methods defined in the parent class (although if overridden, the child's implementation runs).
Why This Question Matters in Interviews
Interviewers ask about inheritance for several important reasons:
- It tests your understanding of core OOP principles that form the foundation of Java
- It reveals your ability to design and structure code in an object-oriented manner
- It explores your knowledge of Java's implementation of inheritance and its limitations
- It often leads into discussions about polymorphism, encapsulation, and abstraction
As someone who's been on both sides of the interview table, I can tell you that strong answers demonstrate not just theoretical knowledge but practical understanding of when and how to apply inheritance effectively. Be prepared to discuss both benefits and drawbacks!
Common Pitfalls and Interview Traps
Watch Out!
Here are some common misunderstandings interviewers may probe for:
- Inheritance vs. Composition: Not understanding when to use inheritance versus composition (has-a relationship)
- Java's Multiple Inheritance Limitations: Confusing how Java handles multiple inheritance through interfaces versus classes
- Constructor Inheritance: Misunderstanding that constructors aren't inherited, but are implicitly called
- Access Modifiers: Forgetting which members can be accessed in subclasses
- Method Hiding vs. Method Overriding: Confusing static method inheritance (method hiding) with instance method overriding
Conclusion
Inheritance is a powerful tool in Java programming that allows for code reuse, establishment of type relationships, and polymorphic behavior. By creating hierarchical relationships between classes, we can model real-world relationships and build more maintainable and logical code structures.
Understanding inheritance isn't just about knowing the syntax of the extends keyword – it's about grasping when to use inheritance, how to design effective hierarchies, and how to balance inheritance with other design principles like composition.
Key takeaways:
- Inheritance establishes an "is-a" relationship between classes
- Java supports single inheritance for classes but allows multiple inheritance through interfaces
- Method overriding is a key aspect of inheritance that enables runtime polymorphism
- Not everything is inherited – private members stay private, and constructors are called but not inherited
- Inheritance should be used judiciously – favor composition when the relationship is more accurately described as "has-a"
Mastering inheritance is essential for any Java developer because it influences how you structure entire applications. While it might seem straightforward at first, implementing it effectively requires experience and a deep understanding of object-oriented design principles.