Inheritance in Object Oriented Programming (Java)

Inheritance is one of the core principles of object-oriented programming (OOP), which help us derive a class from another class or a hierarchy of classes that share a set of attributes and methods. It is a relationship between a superclass (a generalized class) and a subclass (a specialized class), where a subclass inherits data and behavior from the superclass.

  • Inheritance represents the IS-A relationship.
  • A class derived from another class is called a subclass (derived class or child class), and the class from which the subclass is derived is called a superclass (base class or parent class).

Inheritance example in oops

How to use inheritance in Java?

We use the keyword extends to implement inheritance in Java.

class Superclass
{
    //methods and attributes
}

class Subclass extends Superclass
{
    //methods and attributes
}

The inherited attributes or methods in the subclass can be used directly, just like any other attributes or methods. We can also declare new attributes or methods in the subclass that are not in the superclass.

We can also write a new method in the subclass with the same signature as the one in the superclass. This is called method overriding. Similarly, we can also declare an attribute in the subclass with the same name as the one in the superclass, thus hiding it (This is not recommended).

Inheritance code example 1

Inheritance example code java 1

In the above program, when an object of AdvancedCalculator class is created, a copy of all methods and fields of the superclass Calculator acquire memory in this object. That is why by using the object of the subclass we can also access the members of a superclass.

Shared object memory in inheritance

When to use Inheritance in OOPS?

When we find some classes are closely related, we can identify common attributes and methods and add them to a superclass. Then we use inheritance to define subclasses and specialize them with capabilities beyond those inherited from the superclass.

In another scenario, memory and processing resources might be wasted if subclasses are larger than they need to be (i.e., contain too much functionality). So we can extend the superclass that includes the functionality closest to what is required.

Advantages of Inheritance

Code reusability

One of the major purposes of inheritance is code reusability. Suppose there is an existing class A, and we want to create a class B that already includes some part of the code of class A. So instead of writing the same logic inside class B, we can derive class B from class A to reuse the data and methods of class A.

To avoid code duplication

One of the key benefits of inheritance is to minimize the amount of duplicate code by sharing common code among several subclasses. When similar code exists in two related classes, we can move the common code up to a mutual superclass.

Makes code more flexible and extensible

The best thing is that any future changes made to the attributes and methods of a superclass will be automatically applied to the derived class. In other words, using inheritance, the common attributes and methods of all the classes in the hierarchy are declared in a superclass. When changes are required, we only need to make the changes in the superclass and subclasses will inherit those changes. If required, the subclass can also add new attributes add new methods.

Better code structure and management

Inheritance also makes the sub-classes follow a standard interface. It provides a clear code structure that is easy to understand because classes become grouped together in a hierarchical tree structure of parent and child classes.

Help to achieve run time Polymorphism

Inheritance provides the capability of a subclass to override a superclass method by providing a new implementation.

Avoid possible code errors

Without inheritance, we need to make changes to all the existing source code files that contain the same logic. Copying and pasting code from one class to another may spread errors across multiple source code files. So inheritance helps us to avoid possible errors as well.

Preserves the integrity of the superclass

Declaring a subclass does not affect its superclass’s source code. So inheritance preserves the integrity of the superclass. 

Data hiding

The base class can be set to keep some data private so that it cannot be altered by the derived class. This is an example of encapsulation, where access to data is restricted to only the classes that need it for their role.

Disadvantages of Inheritance

Tight coupling

The main disadvantage of using inheritance is that the parent and child class get tightly coupled and both cannot be used independently. The idea is simple: When we inherit something from a parent class, we inherit every public or protected declaration from that parent class, whether we need it or not. A change in the parent class will affect all the child classes.

Slow performance of inherited methods

Inherited methods work slower than normal class methods due to several levels of indirection. It takes the program to jump through all the levels of inherited classes. If a given class has five levels of hierarchy above it, then it will take five jumps to run through a function defined in each of those classes.

Extra maintenance effort on code change

Adding new features during maintenance, we need to change both parent and child classes. If a method is removed from the parent class, then we need to re-factor code in case of using that method. Here things can get a bit complicated. 

Improper use of inheritance can lead to wrong solutions!

Method overriding in Inheritance

We can override the superclass methods so that meaningful implementation of the superclass method can be defined in the subclass. This is also known as runtime polymorphism. In other words, method overriding helps us to implement the idea of polymorphism, which allows different classes to have unique implementations for the same method.

If there is a requirement to override a method:

  • We can write a new method in the subclass by using the same name, same access modifier, same parameters, and same return type as in the superclass.
  • The method in the derived class or classes must have a different implementation.
class Superclass {
    // Other methods and attributes
    ......
    void someMethod() {
        //original implementation
    }
}

class Subclass extends Superclass {
    // Other methods and attributes
    ......
    @override
    void someMethod() {
    //new implementation
    }
}

Typecasting in Java

Typecasting is a process to reference a subclass as an instance of its superclass, essentially treating the subclass as if it were of the superclass type. It is a good way to create a modular code as we can write code that will work for any subclass of the same superclass. There are two types of typecasting:

Upcasting: We can create an instance of a subclass and then assign it to a superclass variable, this is called upcasting.

Dog dog = new Dog();
Animal animal = dog;
// It is okay becasue Dog is also an Animal

Downcasting: When an instance of a superclass is assigned to a subclass object, then it’s called downcasting. We need to explicitly cast this to subclass type.

Dog dog1 = new Dog();
Animal animal = dog1;

// downcast to dog again
Dog dog2 = (Dog) animal;

Some important notes

  • We can upcast any subclass to its superclass but only those objects can be downcast that were originally of the subclass type.
  • The compiler will throw an ClassCastException error at runtime when we try to perform the wrong typecasting. Below are some of the cases:
//ClassCastException case 1
Dog dog = new Dog();
Animal animal = dog;
Lion lion = (Lion) animal;

//ClassCastException case 2
Animal animal = new Animal
Lion lion = (Lion) animal;
  • The upcast object still retains the fields it had and therefore can be added back to make it a valid object of the child class type again.

Constructors in Inheritance

When we instantiate a subclass object, the chain of constructor calls happens: the subclass constructor first invokes the superclass constructor, and the last constructor call completed in the chain is the subclass constructor.

Constructors in inheritance visualization

A compilation error occurs if a subclass constructor calls one of its superclass constructors with arguments that do not match exactly the number and types of parameters specified in one of the superclass constructor declarations.

Inheriting constructors: A subclass inherits all the members (fields, methods, and nested classes) from its superclass. Constructors are not members, so they are not inherited by subclasses. But the constructor of the superclass can be invoked from the subclass either implicitly or by using the keyword super.

super keyword in Java

The super keyword allows the child class to access features from the parent class regardless of their value in the child class. In other words, subclasses can define new local methods or attribute to use or they can use the super keyword to call inherited methods or attributes or the superclass constructor. 

So the super keyword is used for three purposes:

  • Access superclass attributes: super.variablename allows the subclass to access the value of the variablename set in the superclass.
  • Calling a superclass method: super.method() allow the subclass to access the superclass implementation of the method(). This is only required if the child class also has a method with the same name.
  • Using constructors: This allows us to create new instances of the superclass from within a subclass. Calling the super constructor creates a new object that requires all the fields defined in the superclass constructor.
public Dog(String color, String owner){
    super(color); //parent class constructor
    this.owner = owner;
}

Note: When a superclass method is overridden in a subclass, sometimes the subclass version often calls the superclass version to do a portion of the work. Failure to use the keyword super with the superclass method name when referencing the superclass method causes the subclass method to call itself, creating an error called infinite recursion!

Inheritance and access modifiers in Java

As we have seen during encapsulation, access modifiers help us implement an information-hiding mechanism. They can also affect access to data and methods within an inheritance hierarchy.

  • Private attributes or methods can only be accessed within the same class.
  • Methods and attributes without an access modifier can be accessed within the same class and all other classes within the same package.
  • Protected methods and attributes can be accessed within the same class, by all subclasses, and by all classes within the same package.
  • All classes can access public attributes and methods.

Access to data and methods using inheritance and access modifiers in java

Inheritance Java code: Example 2

Animal class code

Java Inheritance code example part 1

Dog class code: Inheriting Animal class

Java Inheritance code example part 2

Lion class code: Inheriting Animal class

Java Inheritance code example part 3

Demo code for inheritance

Java Inheritance code example part 4

Types of Inheritance in Java

Single Inheritance: Subclasses inherit characteristics from a single superclass.

Multilevel Inheritance: A subclass may have its own subclasses. In other words, a subclass of a superclass can itself be a superclass to other subclasses.

Hierarchical Inheritance: A base class acts as the parent superclass to multiple levels of subclasses.

Hybrid Inheritance: A combination of one or more of the other inheritance types. Mostly, it is a situation of single and multiple inheritances. In Java, hybrid inheritance is also not possible with classes, but it can be achieved through Interfaces.

Multiple Inheritance: A subclass may have more than one superclass and inherit characteristics from all of them. Java does not support multiple inheritance with classes, but it can be achieved through Interfaces.

Types of inheritance in java

Default superclass: Except Object class, which has no superclass, every class has one and only one direct superclass (single inheritance). In the absence of any other explicit superclass, every class is implicitly a subclass of the Object class.

Important facts about inheritance in Java

  • Declaring superclass attributes private enables the superclass implementation of these attributes to change without affecting subclass implementations. 
  • Methods of a subclass cannot directly access private members of their superclass. A subclass can only change the state of private superclass attributes through non-private getters and setters methods provided in the superclass.
  • When possible, do not include protected attributes in a superclass. Instead, we can include non-private getters and setters methods that access private attributes.
  • A public method of the superclass cannot become a protected or private method in the subclass. Similarly, a protected method of the superclass cannot become a private method in the subclass. Doing this would break the IS-A relationship because it is required that all subclass objects be able to respond to method calls that are declared public in the superclass.
  • Although inheriting from a class does not require access to the class’s source code, if needed, we should go through its source code to understand how the class is implemented. The idea is simple: we need to ensure that we extend a class that performs well and is securely implemented.
  • If the superclass doesn’t have a default constructor, then the subclass also needs to have an explicit constructor defined. Else it will throw compile time exception. In the subclass constructor, a call to the superclass constructor is mandatory in this case and it should be the first statement in the subclass constructor.
  • The compiler will know that we are overriding a method and if something changes in the superclass method, we will get a compile-time error rather than getting unwanted results at the runtime.

Critical ideas to think about!

  • Why multiple and hybrid Inheritance are not supported in Java?
  • How do we use the final keyword in inheritance?
  • Which SOLID principle uses the idea of Inheritance?

Please share your feedback and insights. Enjoy learning, Enjoy oops!

More from EnjoyAlgorithms

Our weekly newsletter

Subscribe to get free weekly content on data structure and algorithms, machine learning, system design, oops design and mathematics.

Follow us on:

LinkedinMedium

© 2020 EnjoyAlgorithms Inc.

All rights reserved.