Inheritance
Learn Inheritance step by step with clear examples and exercises.
Why This Matters
Inheritance is a fundamental concept in object-oriented programming that allows one class to acquire properties and behaviors from another class. It promotes code reusability, reduces redundancy, and enables the creation of a hierarchical class structure. In Java, inheritance plays a significant role in interview preparation, real-world programming, and solving complex problems efficiently.
Inheritance allows developers to create a hierarchy of classes where each subclass inherits attributes and methods from its superclass, making it easier to manage related objects and their common functionality. By understanding inheritance, you will be able to write more efficient, maintainable, and scalable code in Java.
Prerequisites
Before diving into inheritance, you should have a solid understanding of the following topics:
- Basic Java syntax (variables, methods, operators)
- Classes and objects
- Access modifiers (public, private, protected, default)
- Interfaces
- Exception handling
- Static members
- Abstract classes
- Understanding the difference between abstract classes and interfaces
- Knowledge of polymorphism and method overriding
Core Concept
Inheritance is established by using the extends keyword in Java. The class being extended is called the superclass or base class, while the class that inherits is known as the subclass or derived class. Here's a simple example:
// Superclass (Base Class)
public class Animal {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
// Subclass (Derived Class)
public class Cat extends Animal {
private String breed;
// Inherited method from Animal class
@Override
public void setName(String name) {
super.setName(name); // Calling the inherited method using 'super'
}
// New method in the subclass
public void setBreed(String breed) {
this.breed = breed;
}
public String getBreed() {
return breed;
}
}
In this example, we have an Animal superclass with a single attribute (name) and one method (setName and getName). Then, we create a Cat subclass that inherits the Animal class and adds its own attribute (breed) and methods (setBreed and getBreed). Note that we've used the @Override annotation to ensure that the setName() method in the Cat class correctly overrides the one in the Animal superclass.
Worked Example
Let's consider a more practical example where we have a Vehicle superclass with common attributes like make, model, and year. Then, we create two subclasses: Car and Bike, each with their specific attributes and methods.
// Superclass (Base Class)
public class Vehicle {
private String make;
private String model;
private int year;
// Constructor
public Vehicle(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
// Method to display vehicle details
public void displayDetails() {
System.out.println("Make: " + make);
System.out.println("Model: " + model);
System.out.println("Year: " + year);
}
}
// Subclass (Derived Class)
public class Car extends Vehicle {
private int numberOfDoors;
// Constructor
public Car(String make, String model, int year, int numberOfDoors) {
super(make, model, year); // Calling the constructor of the superclass
this.numberOfDoors = numberOfDoors;
}
// Method to display additional car details
public void displayCarDetails() {
System.out.println("Number of Doors: " + numberOfDoors);
}
}
// Subclass (Derived Class)
public class Bike extends Vehicle {
private String type;
// Constructor
public Bike(String make, String model, int year, String type) {
super(make, model, year); // Calling the constructor of the superclass
this.type = type;
}
// Method to display additional bike details
public void displayBikeDetails() {
System.out.println("Type: " + type);
}
}
In this example, we have a Vehicle superclass with three attributes (make, model, and year) and one method (displayDetails). Then, we create two subclasses: Car and Bike, each inheriting the Vehicle class and adding their specific attributes (numberOfDoors for Car and type for Bike) and methods (displayCarDetails and displayBikeDetails).
Common Mistakes
- Forgetting to call superclass constructors: When creating a subclass constructor, don't forget to call the superclass constructor using
super(). Failing to do so may result in uninitialized instance variables from the superclass. - Incorrect access modifiers: Make sure that you use the correct access modifier for inherited members (e.g., if a method is private in the superclass, it will be private in the subclass). Be aware of the impact of different access modifiers on the visibility and accessibility of inherited members.
- Overriding methods without the 'override' keyword: Although not mandatory, using the
@Overrideannotation can help avoid accidental method overloading. This annotation ensures that the method being overridden matches the signature of the superclass method exactly. - Incorrect method invocation: When calling inherited methods, use
super.methodName(). Failing to do so may result in calling the wrong version of the method or creating a new method with the same name. - Confusing inheritance with composition: While both inheritance and composition are used for code reusability, they serve different purposes. Composition involves creating objects of one class within another, while inheritance creates a hierarchy of classes where one class acquires properties and behaviors from another. Understanding the differences between these two concepts is essential for effective object-oriented design.
Practice Questions
- Create a
Shapesuperclass with attributes (name, color) and methods (getArea, getPerimeter). Then create two subclasses:CircleandRectangle. Implement the getArea() method for both subclasses. - Given the following classes:
public class Person {
private String name;
private int age;
// Constructor, getters, and setters
}
public class Employee extends Person {
private double salary;
// Constructor, getters, and setters
}
Write a program that creates an Employee object, sets its properties, and displays the employee's details.
FAQ
Can a class inherit from multiple classes in Java?
- No, Java supports single inheritance only (one superclass per subclass). However, you can achieve multiple inheritance using interfaces.
What is the difference between abstract classes and interfaces in Java?
- Abstract classes can have instance variables, constructors, and default methods, while interfaces cannot. Abstract classes can be instantiated, but interfaces cannot. Interfaces are used for defining contracts, while abstract classes are used for creating generalized parent classes with some implemented functionality.
Can a class inherit from an interface in Java?
- Yes, a class can implement multiple interfaces but extend only one class (single inheritance).
What is method hiding and overriding in Java?
- Method hiding occurs when a subclass has a method with the same name and signature as a method in its superclass. In this case, the subclass method hides (or masks) the superclass method. Overriding, on the other hand, involves a subclass providing a new implementation for an inherited method from the superclass by using the
@Overrideannotation.
How does Java handle method overloading and overriding?
- Method overloading in Java refers to having multiple methods with the same name but different parameters. The correct overloaded method is selected based on the arguments passed during runtime. Method overriding, as mentioned earlier, involves a subclass providing a new implementation for an inherited method from the superclass by using the
@Overrideannotation. The correct overridden method is selected based on the object's runtime type.