Welcome to another core pillar of Object-Oriented Programming (OOP): Inheritance! This powerful concept allows you to define a new class based on an existing class, establishing a hierarchical relationship. Inheritance is all about reusability and creating a logical "is-a" relationship between classes.
Imagine you're designing a system for different types of vehicles. All vehicles have some common properties (like speed, color) and behaviors (like accelerating, braking). Instead of redefining these common elements for Car, Bicycle, Motorcycle, etc., inheritance allows you to create a general Vehicle class and then have Car, Bicycle, and Motorcycle "inherit" those common features.
1. The "Is-A" Relationship
Inheritance models the "is-a" relationship.
A
Caris aVehicle.A
Dogis anAnimal.A
Studentis aPerson.
This relationship is fundamental to understanding when to use inheritance. If a class A "is a" type of class B, then A can inherit from B.
2. Parent Class (Superclass) and Child Class (Subclass)
In an inheritance relationship:
Parent Class / Superclass / Base Class: The existing class whose features are being inherited. (e.g.,
Vehicle,Animal,Person)Child Class / Subclass / Derived Class: The new class that inherits features from the parent class. (e.g.,
Car,Dog,Student)
A subclass inherits all the non-private members (fields and methods) of its superclass. This means you don't have to rewrite the common code.
3. The extends Keyword
In Java, you use the extends keyword to establish an inheritance relationship.
Syntax:
class Subclass extends Superclass {
// Subclass specific fields and methods
}
Example:
// Superclass / Parent Class
class Animal {
String species;
int age;
public Animal(String species, int age) {
this.species = species;
this.age = age;
System.out.println("Animal constructor called.");
}
public void eat() {
System.out.println(species + " is eating.");
}
public void sleep() {
System.out.println(species + " is sleeping.");
}
}
// Subclass / Child Class
class Dog extends Animal {
String breed;
public Dog(String breed, int age) {
// Call the superclass constructor using 'super()'
super("Dog", age); // Calls Animal's constructor
this.breed = breed;
System.out.println("Dog constructor called.");
}
// Dog's specific method
public void bark() {
System.out.println("Woof woof!");
}
public static void main(String[] args) {
Dog myDog = new Dog("Golden Retriever", 3);
System.out.println("My dog's species: " + myDog.species); // Inherited field
System.out.println("My dog's age: " + myDog.age); // Inherited field
System.out.println("My dog's breed: " + myDog.breed); // Dog's own field
myDog.eat(); // Inherited method from Animal
myDog.sleep(); // Inherited method from Animal
myDog.bark(); // Dog's own method
}
}
Explanation:
The
DogclassextendstheAnimalclass. This meansDoginheritsspecies,age,eat(), andsleep()fromAnimal.The
Dogclass also has its own unique field (breed) and method (bark()).In the
Dogconstructor,super("Dog", age)is used to call the constructor of theAnimalsuperclass. This is crucial for initializing the inherited parts of theDogobject.
4. Benefits of Inheritance
Code Reusability: The most significant benefit. You write common code once in the superclass, and all subclasses automatically get access to it. This saves development time and reduces errors.
Polymorphism: Inheritance is a prerequisite for polymorphism, allowing objects of different classes to be treated as objects of a common superclass. (More on this in a future lesson!)
Logical Hierarchy: Helps organize classes in a logical, tree-like structure, making your code easier to understand and manage.
Maintainability: Changes to common functionality in the superclass automatically propagate to all subclasses, simplifying updates.
5. What is Inherited (and What Isn't)?
What is Inherited?
Public fields and methods: These are fully accessible and usable by the subclass.
Protected fields and methods: These are accessible within the same package AND by subclasses, even if the subclass is in a different package.
What is NOT Directly Inherited?
Private members: Private fields and methods of the superclass are NOT directly accessible by the subclass. However, they can be accessed indirectly if the superclass provides public or protected getter/setter methods for them (which is a good practice, linking back to encapsulation!).
Constructors: Constructors are special methods used to initialize objects, and they are NOT inherited. Each class must define its own constructors. However, a subclass's constructor must call a superclass constructor (explicitly or implicitly).
6. Method Overriding
Method overriding occurs when a subclass provides its own specific implementation of a method that is already defined in its superclass. The method in the subclass must have the exact same signature (name, return type, and parameters) as the method in the superclass.
@OverrideAnnotation: It's good practice (and highly recommended) to use the@Overrideannotation above a method that you intend to override. This helps the compiler check if you've correctly overridden the method, catching typos or signature mismatches.
Example:
class Vehicle {
String brand;
public Vehicle(String brand) {
this.brand = brand;
}
public void start() {
System.out.println(brand + " vehicle is starting.");
}
public void stop() {
System.out.println(brand + " vehicle is stopping.");
}
}
class Car extends Vehicle {
int numberOfDoors;
public Car(String brand, int numberOfDoors) {
super(brand); // Call Vehicle's constructor
this.numberOfDoors = numberOfDoors;
}
@Override // Good practice: indicates this method overrides a superclass method
public void start() {
System.out.println(brand + " car is starting with a key.");
}
// Car-specific method
public void drive() {
System.out.println(brand + " car is driving.");
}
public static void main(String[] args) {
Vehicle genericVehicle = new Vehicle("Generic Motors");
genericVehicle.start(); // Output: Generic Motors vehicle is starting.
Car myCar = new Car("Toyota", 4);
myCar.start(); // Output: Toyota car is starting with a key. (Overridden method)
myCar.stop(); // Output: Toyota vehicle is stopping. (Inherited method)
myCar.drive(); // Output: Toyota car is driving. (Car's own method)
}
}
Explanation:
Both
VehicleandCarhave astart()method.Car'sstart()method is marked with@Overrideand provides a more specific implementation for how aCarstarts.When
myCar.start()is called, theCarclass's version ofstart()is executed. WhengenericVehicle.start()is called,Vehicle's version is executed.
7. The super Keyword
The super keyword in Java is used to refer to the superclass (parent class) of a subclass. It has two main uses:
Calling Superclass Constructor:
super()is used to invoke the constructor of the immediate superclass.This must be the first statement in the subclass's constructor.
If you don't explicitly call
super(), Java's compiler will automatically insert a call to the superclass's no-argument constructor (super();) if one exists. If the superclass only has parameterized constructors, you must callsuper()explicitly with the correct parameters.
Calling Superclass Methods:
super.methodName()is used to call a method from the superclass, even if that method has been overridden in the current subclass. This allows you to extend or augment the superclass's behavior.
Example:
class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person constructor called.");
}
public void introduce() {
System.out.println("Hi, my name is " + name + " and I am " + age + " years old.");
}
}
class Employee extends Person {
String employeeId;
double salary;
public Employee(String name, int age, String employeeId, double salary) {
super(name, age); // Call to Person's constructor - MUST BE THE FIRST STATEMENT
this.employeeId = employeeId;
this.salary = salary;
System.out.println("Employee constructor called.");
}
@Override
public void introduce() {
super.introduce(); // Call the introduce() method from the Person superclass
System.out.println("I am an employee with ID: " + employeeId + " and earn $" + salary);
}
public void work() {
System.out.println(name + " is working.");
}
public static void main(String[] args) {
Employee emp = new Employee("Jane Doe", 30, "EMP123", 75000.00);
emp.introduce(); // This calls the overridden introduce() in Employee
// which in turn calls the Person's introduce()
emp.work();
}
}
Explanation:
In
Employee's constructor,super(name, age)ensures that thenameandagefields (inherited fromPerson) are initialized correctly by callingPerson's constructor.In
Employee'sintroduce()method,super.introduce()calls theintroduce()method defined in thePersonclass, allowing the employee to first introduce themselves as a person, and then add their employee-specific details.
8. Single Inheritance in Java
Java classes support single inheritance only. This means a class can extend only one direct superclass.
// Valid
class Dog extends Animal {}
// Invalid - Will cause a compile-time error
// class HybridCar extends ElectricCar, GasolineCar {}
While a class can only extend one other class, an inheritance hierarchy can be very deep (e.g., Object -> Animal -> Dog -> GoldenRetriever). Java achieves similar concepts to multiple inheritance through interfaces (which will be covered in a later lesson).
9. The final Keyword with Inheritance (Brief Mention)
The final keyword can impact inheritance:
final class: A class declaredfinalcannot be extended by any other class. (e.g.,Stringclass).final method: A method declaredfinalin a superclass cannot be overridden in any subclass.
Key Takeaways
Inheritance establishes an "is-a" relationship between classes.
The
extendskeyword is used to create a subclass (child) from a superclass (parent).Subclasses inherit public and protected members from their superclass.
Private members and constructors are NOT inherited (though constructors must be called using
super()).Method Overriding allows a subclass to provide its own specific implementation of an inherited method, often indicated by the
@Overrideannotation.The
superkeyword is used to call the superclass's constructor (as the first line in a subclass constructor) or to invoke a superclass method.Java supports single inheritance for classes (a class can only extend one other class).
finalclasses cannot be extended, andfinalmethods cannot be overridden.
Exercise
To solidify your understanding of inheritance, try the following exercise:
Problem: Design a simple hierarchy for Shapes.
Create a Superclass
Shape:It should have
privateinstance variables forcolor(String) andisFilled(boolean).A constructor that takes
colorandisFilled.Public getter methods for
colorandisFilled.A
publicmethodgetArea()that returns adouble. For theShapeclass itself, this method should return0.0or throw anUnsupportedOperationException, as a generic shape doesn't have a concrete area.A
publicmethodgetPerimeter()that also returns0.0or throws anUnsupportedOperationException.A
publicmethoddisplayInfo()that prints the shape's color and whether it's filled.
Create a Subclass
Circlethat extendsShape:It should have a
privateinstance variable forradius(double).A constructor that takes
color,isFilled, andradius. Remember to usesuper()to call theShapeconstructor.Public getter and setter methods for
radius. Ensureradiusis positive in the setter.Override the
getArea()method to calculate the area of a circle (πr2). (You can useMath.PIandMath.pow(radius, 2)).Override the
getPerimeter()method to calculate the circumference of a circle (2πr).Override
displayInfo()to callsuper.displayInfo()and then add the circle's radius, area, and perimeter.
Create a Subclass
Rectanglethat extendsShape:It should have
privateinstance variables forwidth(double) andheight(double).A constructor that takes
color,isFilled,width, andheight. Remember to usesuper().Public getter and setter methods for
widthandheight. Ensure both are positive in their setters.Override the
getArea()method to calculate the area of a rectangle (width * height).Override the
getPerimeter()method to calculate the perimeter of a rectangle (2×(width+height)).Override
displayInfo()to callsuper.displayInfo()and then add the rectangle's width, height, area, and perimeter.
In a
mainmethod (in any of the classes or a separate test class):Create objects of
CircleandRectangle.Call
displayInfo()for each object.Try to use setters to modify dimensions and observe the new calculated area/perimeter after calling
displayInfo()again.(Optional but good practice): Try to create a
Shapeobject directly and callgetArea()orgetPerimeter()to see theUnsupportedOperationException(if you implemented it that way).
This exercise will give you practical experience with defining inheritance hierarchies, calling superclass constructors, overriding methods, and using encapsulated data
Key Takeaways
-
The primary benefit is writing common code once in a superclass (parent) and having subclasses (children) automatically inherit those features, reducing redundancy.
-
Subclasses inherit all public and protected members (fields and methods) from their superclass.
-
private members of the superclass are not directly accessible by subclasses (though they can be accessed via public/protected getters/setters).
Constructors are not inherited; each class must define its own constructors.