Inheritance in Java

Inheritance is one of the fundamental concepts in object-oriented programming. In Java, we can define a child class that reuses (inherits), extends, or modifies the behavior of a parent class (or base class.). This child class also known as subclass.

OOP UML Inheritance & Polymorphism.png

Inheritance in Polygon as Base Class

Inheritance Modifiers

Java introduces the following class-level statements and modifiers to support inheritance:

  • extends statement is used to declare a new class based on an existing class.
  • final statement is to prevent a class as a base class, means no class can extend it.
  • abstract statement specifies that the class is intended for use as a base class only. Instances of abstract classes cannot be created directly (initialize); they must be used as a base class (or extended).

The following section describes some of the rules for inheritance, and the modifiers you can use to change the way classes inherit or are inherited:

  • By default, all classes are inheritable unless marked with the final keyword.
  • Java support single inheritance only. That is, a class can only inherit from a single class. However, inheritance is transitive, which allows you to define an inheritance hierarchy for a set of types. In other words, type Duck can inherit from type WaterBird, which inherits from type Bird, which inherits from the base class type Animal. Because inheritance is transitive, the protected and public members of type Animal are available to type Duck.

Overriding Methods in Subclasses

By default, a subclass inherits methods from its base class. If an inherited method has to behave differently in the subclass it can be overridden. That is, you can define a new implementation of the method in the subclass. The following annotation and modifiers are used to control how methods are overridden:

  • @Override will overrides an overridable method defined in the base class. @Override is optional, to validate that a base class method with the same signature is really exists.
  • final statement prevents a method from being overridden in an inheriting class.
  • abstract statement requires that a derived class override the method. When the abstract keyword is used, the method is declared without an implementation (without braces, and followed by a semicolon) . If a class includes abstract methods, then the class itself must be declared abstract.

Following is an example demonstrating Java inheritance. In this example, you can observe two classes namely BaseClass and SubClass.

BaseClass .java
import java.math.BigDecimal;

public class BaseClass {
    
    public BigDecimal calculatePayment(int number, BigDecimal price) {
        return new BigDecimal(number).multiply(price);
    }
    
    public String settlePayment(BigDecimal payment) {
        return payment.setScale(2, BigDecimal.ROUND_HALF_UP).toString() + " is settled";
    }
}
                    

SubClass.java
import java.math.BigDecimal;

public class SubClass extends BaseClass {
    
    @Override
    public BigDecimal calculatePayment(int number, BigDecimal price) {
        // Call the method in the base class and modify the return value. 
        return super.calculatePayment(number, price).multiply(new BigDecimal("2"));
    }
}
                    

Using extends keyword, the SubClass inherits the methods "calculatePayment" and "settlePayment" of BaseClass. Method calculatePayment(int number, BigDecimal price) is overrided in SubClass.

And below is an example demonstrating Java final class. When a subclass extends from Final, it will throw compilation error: cannot inherit from final FinalClass

FinalClass.java
public final class FinalClass {
 
    public String finalSay() {
        return "I'm final";
    }
}
                    

The super Keyword

You can use the super keyword to call methods in a base class when you override methods in a subclass. As in above example, SubClass overrides method calculatePayment(...) inherited from BaseClass. The overridden method can call method calculatePayment(...) in BaseClass and modify the return value.

The following list describes restrictions on using super:

  • super refers to the immediate base class and its inherited members, super cannot be used to access base class members that are marked as private.
  • The method that super qualifies does not have to be defined in the immediate base class; it may instead be defined in an indirectly inherited base class. During compilation, compiler will check the reference qualified by super, so base class (or classes) must contain a method matching the name and the parameters in the call.
  • super is a keyword, not a real object. super cannot be assigned to a variable, passed to methods, or used in a comparison.
  • You cannot use super to call an abstract method in base class (this is no brainier).
  • super cannot be used to qualify itself. Therefore, the following code is not valid:
    super.super.doSomething();

When Do We Use Inheritance?

Inheritance is a useful programming concept, but we must understand on how to use it effectively. In Java, sometimes we struggle when to use interface, or when to use base class (inheritance). In some cases, interfaces can do the job better. We must understand the reason behind each approach. Inheritance is a good choice when:

  • Your inheritance hierarchy represents an "is-a" relationship and not a "has-a" relationship (example: Bird is an Animal)
  • You can reuse code from the base classes.
  • You need to apply the same class and methods to different data types.
  • The class hierarchy is reasonably shallow, and other developers are not likely to add many more levels.
  • You want to make global changes to derived classes by changing a base class.