DEV Community

Cover image for OOP Series: Inheritance in Java
Suleiman Ibrahim
Suleiman Ibrahim

Posted on • Updated on

OOP Series: Inheritance in Java

INTRODUCTION

Object-oriented Programming (OOP) is a programming paradigm solemnly based on the concepts of Objects and Classes. It allows the group or collection of related objects in a class and create functionalities for use by the class with the help of methods.
Object-oriented programming has four pillars or principles known as:

  • Inheritance
  • Polymorphism
  • Data Abstraction
  • Encapsulation

These four principles qualifies a language to be called Object-oriented. In this tutorial, we will be briefly looking at Inheritance.

CONTENT

  1. Prerequisite knowledge
  2. Inheritance
  3. Direct and Indirect superclass
  4. Composition vs. Inheritance
  5. The Student class
  6. Conclusion

PREREQUISITE KNOWLEDGE

This article explains the concept of inheritance using Java programming language. Therefore the audience is expected to have a basic knowledge of the following in Java:

  • Basic understanding of OOP.
  • Methods.
  • Classes and objects.

If you are new to Java or have a rusty knowledge in it, you can do well to check out Hello World Breakdown in Java where we walk through the building blocks of a simple Java program.

INHERITANCE

Inheritance is one of the four pillars of Object-oriented Programming paradigm. Inheritance is a concept that allows new classes to be created from an existing class by acquiring or inheriting the properties of that class and possibly add more functionality to the new class. Take for example a family hierarchy, in most cases the children exhibit some of the characteristics of either the mother or the father. Some of these characteristics might include

  • skin color
  • height
  • size
  • ...

In this case we can safely say that the child inherits those characteristics from the parent since those characteristics can be found in the parent. In some cases, the child might have not only the characteristics of the parent, but the grandparents and so. In other cases, the child might display the characteristics of both the parent and grandparent and still display their own personal attributes which makes the child unique. This is the basic concept of inheritance in OOP. Inheritance allow us to derive a new class from an existing class. The existing class which the new classes can be derived from is called the superclass in Java. It is also known as the base class or parent class in some other languages like C++. While the new class that is derived is called the subclass in Java. It is also known as derived or child class in some other object-oriented languages as well.

Some of the advantages inheritance provides are:

  • Reusability: It allows the reuse of code written by us or other programmers thereby avoiding reinventing the wheel.
  • Flexibility: When an update need to be made to a group of classes, inheritance allows us to just update the superclass only instead of making the update on all of the affected classes individually.
  • Redundancy: Inheritance allows us to write less code and build upon the previous ones thereby reducing redundancy and encouraging cleaner code.
  • Data hiding: The use of private instance variables as well as set and get methods to access them allows restricted access to the variables of a class.

Direct and Indirect Superclass

Super classes can be categorized into two types, the direct superclass and the indirect super class. The direct superclass is the immediate class in which a new class inherits from in the class hierarchy. It is the class immediately above the new class in the inheritance hierarchy tree. The indirect superclass is any class in the inheritance hierarchy above the direct superclass. The new class does not directly inherit any attribute from an indirect superclass but rather get these attributes from the direct superclass.
All classes in Java directly or indirectly inherits from the Object class which can be found in java.lang.Object. The Object class provides some functionalities such as equals() (which checks weather two objects are equal), getClass() (which returns the runtime class of a particular object), toString() (which returns a string consisting of the name of the class of which the object is an instance and the unsigned hexadecimal representation of the hash code of the object), notify(), notifyAll() and many others which can be utilized by its subclasses.

Composition vs. Inheritance

Java only provides support for single inheritance unlike other programming languages like C++ that supports both single and multiple inheritance. In single inheritance, a class can be derived from only one direct superclass, while in multiple inheritance, a class can be derived from multiple superclass.
The relationship that can exists between two class can either be is-a relationship or has-a relationship. Is-a relationship implies inheritance while has-a relationship implies composition.
In inheritance, an object of a subclass can as well be treated as an object of the super class. For instance, if a Rectangle class extends the Shape class, then the Rectangle is-a Shape and can therefore be treated as a Shape. In composition, a class is said have references to objects of other classes as its member. For example, each student has a course of study, so it is necessary to include a reference of the Course class as a member of the Student class. In this case, we can safely say that each Student has-a a Course.

The Student Class

In this article, we will be considering the Student class to explain the concept of inheritance. Below is an image that shows some of the classes that can be derived from Student class and their hierarchical relationship.
Student Hierarchy

Here, the Student class is considered to be the base class of all classes and Undergraduate and Graduate inherit directly from the Student class. FirstYear and SecondYear inherits directly from the Undergraduate class. In this case, FirstYear and SecondYear are said to have an indirect inheritance to the Student class and a direct inheritance to the Undergraduate class since FirstYear and SecondYear inherits direct from the Undergraduate but not directly from the Student class.

package Inheritance;
// Student.java
public class Student {

    private String firstName;
    private String lastName;
    private String studentId;

    public Student(String firstName, String lastName, String studentId) {
        // validate first name
        if (!(firstName.matches("[A-Z][a-zA-Z]+"))) 
            throw new IllegalArgumentException("Invalid first name.");

        // validate last name
        if (!(lastName.matches("[a-zA-Z]+(['-][a-zA-Z]+)*")))
            throw new IllegalArgumentException("Invalid last name");

        // validate student id
        if (studentId.length() == 0)
            throw new IllegalArgumentException("Invalid ID");

        this.firstName = firstName;
        this.lastName = lastName;
        this.studentId = studentId;
    }

    // set and get methods

    public void setFirstName(String firstName) {
        if (!(firstName.matches("[A-Z][a-zA-Z]+"))) 
            throw new IllegalArgumentException("Invalid first name.");

        this.firstName = firstName;        
    }

    public String getFirstName() {
        return firstName;
    }

    public void setLastName(String lastName) {
        if (!(lastName.matches("[a-zA-Z]+(['-][a-zA-Z]+)*")))
            throw new IllegalArgumentException("Invalid last name");

        this.lastName = lastName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setStudentID(String studentId) {
        if (studentId.length() == 0)
            throw new IllegalArgumentException("Invalid ID");

        this.studentId = studentId;
    }

    public String getStudentId() {
        return studentId;
    }

    @Override
    public String toString() {
        return String.format("%s: %s%n%s: %s%n%s: %s%n", 
                "Student ID", getStudentId(), "First Name", getFirstName(),
                "Last Name", getLastName());
    }
}
Enter fullscreen mode Exit fullscreen mode

Student.java first start by declaring and initializing its instance private instance fields. Recall that any field or member declared as private is only accessible to the class alone, so therefore the instance fields declared in the Student class canโ€™t be accessed outside the class. This is where the set and get methods come to help. In this case, the set methods first validate the values entered before making it available to the class for use and throws an error with a meaningful message to the user. This means that any class extending the Student class needs not to validate its first name, last name and student id anymore because the Student class has already handled that. This is one of the advantage inheritance provides. It prevents programmers from reinventing the wheel. This speed up production and also reduces the complexity of a program since some implementations are done in the superclass.
The toString() method at the end also shows that Student class is overriding one of its superclass method using the @Override annotation. Recall that every class in Java by default and directly or indirectly extends the Object superclass, so we donโ€™t have to explicitly extend the Object class at the Student class declaration. This also shows that subclasses can modify or increase the capabilities of the superclass members by simply overriding them in the subclass.

The next program containing the Undergraduate class inherits the properties of the Student class.

package Inheritance;

// Undergraduate.java
public class Undergraduate extends Student{

    private int year;

    public Undergraduate(String firstName, String lastName, String studentId, int year) {
        super(firstName, lastName, studentId);

        this.year = year;
    }

    // set and get methods
    public void setYear(int year) {
        this.year = year;
    }

    public int getYear() {
        return year;
    }

    @Override
    public String toString() {
        return String.format("%sYear: %s%n%s%n", super.toString(), getYear(), "Undergraduate Student");
    }
}

Enter fullscreen mode Exit fullscreen mode

Undegraduate.java first starts off at the class declaration by extending or inheriting from the Student class using the extends keyword. extends is a keyword in Java used implement inheritance and it can only extend one class since Java only supports single inheritance.
The program continued by declaring an instance field and then declaring the constructor with parameters. Then next to the initializing the superclass variable using the super keyword, immediately followed by set of parentheses consisting of the superclass constructor arguments. super is also a keyword in Java and it is used by subclasses to initialize the variables of the superclass constructor. Java requires that the first task of any subclass constructor is to call the constructor of its direct superclass.
Over here, the firstName, lastName and studentId needs not to be validated anymore because superclass has handled that already and they can be accessed using the super. followed by their get methods. Similar to the Student class, Undergraduate class also has its own toString() method which uses super.toString() to access the direct superclass String representation. Members of superclass can be accessed using the super. followed by the member to access.

The next program containing the Graduate class also inherits from the student class

package Inheritance;

// Graduate.java
public class Graduate extends Student{

    private String program;

    public Graduate(String firstName, String lastName, String studentId, String program) {
        super(firstName, lastName, studentId);

        this.program = program;
    }

    // set and get methods

    public void setProgram(String program) {
        this.program = program;
    }

    public String getProgram() {
        return program;
    }

    @Override
    public String toString() {
        return String.format("%sProgram: %s%n%s%n", 
                super.toString(), getProgram(), "Graduate Student");
    }
}
Enter fullscreen mode Exit fullscreen mode

Similar to the Undergraduate class, Graduate class also extends the Student class and it contains instance variable as well as a constructor that takes in values to be used to initialize the super class variables. The implantation of the Graduate class is similar to Undergraduate class. The main purpose of the Graduate class is to demonstrate how multiple subclasses can inherit from a single superclass โ€“ Student class.
The next program contains the StudentTest class that implements both the Undergraduate and Graduate class.

package Inheritance;

// StudentTest.java
public class StudentTest {

    public static void main(String[] args) {

        // Declare and initialize objects
        Undergraduate undergraduate = new Undergraduate("Bush", "White", "1021", 3);
        Graduate graduate = new Graduate("Alexa", "Brown", "1102", "Masters");

        // print string representation of objects
        System.out.println(undergraduate);
        System.out.println(graduate);
    }
}
Enter fullscreen mode Exit fullscreen mode

Class StudentTest creates objects of both the Undergraduate and Graduate class and passing the constructor values as well. It then proceeds to printing the values of the objects created. Notice that the toString() method was not explicitly used here to print the String representation of the objects. This is because they have already been customized from the Undergraduate and Graduate class respectively.

Below is the output of executing StudentTest.java

StudentTest.java output

The output first displays the details of the undergraduate student followed by the graduate student. Notice that among the undergraduate and graduate student details is an additional detail specifying the type of student which was not included in the constructor when created.
These details and the format of the was all included in the toString() method of the respective subclasses.

Conclusion

Inheritance is a broad topic in Java and other object-oriented programming languages as well. This article implemented the concept of inheritance using the Student class with Graduate and Undergraduate class as subclasses. We learned about the concept of superclass and subclass and its usage in inheritance. Similarly, we used some keywords such as extends, super as well as using the @override annotation to add functionalities to the default toString method of every class. We now moved to using the set and get methods to access the private field of a superclass as well as validating the values before setting them.
In the next article, we will be building upon the concept of inheritance by introducing Polymorphism - the ability of objects to exists in many form, which is also a core principle of object-oriented programming. Stay tuned!

Discussion (0)