Inheritance in Java

Inheritance in Java

This article covers an important OOP principle: inheritance.

Inheritance is an important OOP principle because it allows you to create a new class from an existing class. The existing class is known as the base class, superclass, or parent class and the new class is the derived class, sub-class, or child class.

Why would we need to do this? Whenever we come across an IS-A relationship between objects, that's a good opportunity to implement inheritance. Examples include:

  • Mammal IS AN Animal.

  • A Car IS A Vehicle.

  • A Smartphone IS A Device.

In these cases, the base classes are Animal, Vehicle, and Device while the derived classes are Mammal, Car, and Smartphone respectively.

Derived classes inherit ALL public member variables and methods.

Another good example is class. Class actually inherits its implementation from the Java Object class. Find out more about Object here.

Note: Some classes cannot be inherited. Such classes are defined with the keyword, final. An example of such a class is the built-in Integer class.

extends keyword

To implement inheritance, you have to use the keyword extends.

Example:

// Base Class Vehicle
class Vehicle {

  // Private Fields
  private String manufacturer; 
  private String make; 
  private int year;      
  private int engineSize;   


  // Parameterized Constructor
  public Vehicle(String manufacturer, String make, int year, int engineSize) {
    this.manufacturer = manufacturer;
    this.make = make;
    this.year = year;  
    this.engineSize = engineSize; 
  }

  // public method to print details
  public void printCarDetails() {
    System.out.println("Manufacturer: " + make);
    System.out.println("Make: " + color);
    System.out.println("Year: " + year);
    System.out.println("Engine Size: " + model);
  }

}

// Derived Class Car
class Car extends Vehicle {

  // Private field
  private String bodyShape;

  // Parameterized Constructor
  public Car(String manufacturer, String make, int year, int engineSize, String bodyShape) {
    super(manufacturer, make, year, engineSize);  //call parent class constructor
    this.bodyShape = bodyShape;       
  }

  public void carDetails() {  //added specific details of the car
    printCarDetails();         //call method from parent class
    System.out.println("Body Shape: " + bodyShape);
  }

}

class Main {

  public static void main(String[] args) {
    Car mercedes = new Car("Mercedes", "Black", 2022, "S63", "Coupe"); //creation of car Object
    mercedes.carDetails(); //call method to print details
  }

}

super keyword

In this article, we talked about the this keyword and how it refers to an instance of the current class. Similarly, we introduce the super keyword. The super keyword is used to refer to all the members and methods of the base class inside the derived class.

It's mainly used to implement inheritance.

Let's look at some use cases.

Access super class members

Example:

class Vehicle { // Super class  

  int topSpeed = 180; // Top Speed defined inside the super class

} 


class Car extends Vehicle { // Sub class extending from super class

  int topSpeed = 260; // Top Speed defined inside the sub class

  public void displayMaxSpeed() { 
    // Access the super class field inside the sub class
    System.out.println("Top speed from the Vehicle class: " + super.topSpeed); 
    // Without using super 
    System.out.println("Top speed from the Car class: " + topSpeed); 

  } 

} 

class Main {

  public static void main(String[] args) { 
    Car mercedes = new Car(); 
    mercedes.displayMaxSpeed(); 
  } 

}

Access super class methods

Example:

class SuperDisplay {  // Super class

  public void display() {   // display method inside SuperClass
    System.out.println("I am from the Super Class");
  }

} 

class SubDisplay extends SuperDisplay { // sub class SubDisplay extending from SuperDisplay

  public void display() { // display method inside SubClass
    System.out.println("I am from the Sub Class");
  } 

  public void print(){
    System.out.println("The display() method with super:");
    super.display();  //call the display() of SuperDisplay(SuperClass)
    System.out.println("The display() method without super:");
    display();        //call the display() of SubDisplay(SubClass)
  }

} 

class Main {

  public static void main(String[] args) {
    SubDisplay childclass  = new SubDisplay(); 
    childclass.print(); 
  }

}

Access constructors

In this article, we cover how constructors are fundamental to creating objects from classes. super can also be used to access constructors from a superclass inside a subclass.

Note: In a constructor, we can include a call to super() or this() but not both.

Example:

// Base Class Vehicle
class Vehicle {

  // Private Fields
  private String manufacturer; 
  private String make; 
  private int year;      
  private int engineSize;   


  // Parameterized Constructor
  public Vehicle(String manufacturer, String make, int year, int engineSize) {
    this.manufacturer = manufacturer;
    this.make = make;
    this.year = year;  
    this.engineSize = engineSize; 
  }

  // public method to print details
  public void printCarDetails() {
    System.out.println("Manufacturer: " + make);
    System.out.println("Make: " + color);
    System.out.println("Year: " + year);
    System.out.println("Engine Size: " + model);
  }

}

// Derived Class Car
class Car extends Vehicle {

  // Private field
  private String bodyShape;

  // Parameterized Constructor
  public Car(String manufacturer, String make, int year, int engineSize, String bodyShape) {
    super(manufacturer, make, year, engineSize);  //call parent class constructor
    this.bodyShape = bodyShape;       
  }

  public void carDetails() {  //added specific details of the car
    printCarDetails();         //call method from parent class
    System.out.println("Body Shape: " + bodyShape);
  }

}

class Main {

  public static void main(String[] args) {
    Car mercedes = new Car("Mercedes", "Black", 2022, "S63", "Coupe"); //creation of car Object
    mercedes.carDetails(); //call method to print details
  }

}

super can be used with no arguments to refer to a default constructor or with parameters (super(parameters)) to render to a parameterized constructor.

Types of inheritance

There are 5 types of inheritance:

  1. Single

  2. Multi-level

  3. Hierarchical

  4. Multiple

  5. Hybrid

Single Inheritance

In single inheritance, there is only a single class extending from another class. Throughout this article, we have only covered single inheritance.

Multi-level inheritance

When a derived class inherits from a derived which itself inherits from a base class, this type of inheritance is called Multilevel Inheritance. Classes can be extended to any further levels per requirements.

Let's implement the following:

  • A Car IS A Vehicle

  • A Tesla IS A Car

class Vehicle {  //Base class  

  private int topSpeed;

  public void setTopSpeed(int topSpeed) {
    this.topSpeed=topSpeed;
    System.out.println("Top Speed: "+ topSpeed);
  }

}

class Car extends Vehicle { // Derived from Vehicle, Base for Tesla

  public void pushToStart() {
    System.out.println("The car has started."); 
  } 

} 

class Tesla extends Car { // Derived from Car and can be a base to any further class

  public void chargeVehicle() {
    System.out.println("The car is charging"); 
  } 

} 

class Main {

  public static void main(String[] args) {
    Tesla modelS = new Tesla(); 
    modelS.setTopSpeed(260);
    modelS.pushToStart();
    modelS.chargeVehicle();
  }

}

Hierarchical inheritance

When more than one class extends from the same base class, it is referred to as hierarchical inheritance.

  • A Mammal IS AN Animal

  • A Reptile IS AN Animal

Multiple Inheritance

When a class is derived from more than one base class, i.e. when a class has more than one immediate parent class, this type of inheritance is called Multiple Inheritance.

  • A Human IS AN Animal

  • A Human IS A Mammal also

Hybrid Inheritance

A combination of Multiple and Multi-level inheritance.

  • A Male IS A Human

  • A Female IS A Human

  • A Child comes from Male and Female

Multiple and Hybrid inheritances are applicable using interfaces only.

Conclusion

Inheritance is useful for several reasons.

Inheritance can help you avoid duplicating your code which makes re-usability and maintenance easier. You don't have to write the same code over and over. You can simply inherit from the base class and it will propagate to all objects you create from that class. This saves time and effort.

In addition, inheritance makes your code extensible. Using inheritance, one can extend the base class logic in the derived class. This is an easy way to upgrade or enhance specific parts of a product without changing the core attributes. An existing class can act as a base class to derive a new class having upgraded features.

We can also implement encapsulation by keeping members private and only allowing methods in the derived class to interact with these private members.