Java Overriding & Introduction to super keyword

What is Overriding?

When a child class inherits methods from its parent class, the child gets the privilege to have its own unique implementation of the inherited method. In short, a child class can override the inherited method. This ability to define a unique version of the inherited behaviour is called Overriding.

The concept of overriding is inspired by natural evolution. Every species has inherited some behaviour and evolved its own version of the behaviour.

Let us look at an example:

class Menu{
    double price;
    void setPrice(double param){
        System.out.println("I am the version of class Menu");
        price=param;
    }
}

class SurchargeMenu extends Menu{
   @Override
    void setPrice(double param){
        System.out.println("I am the version of class SurchargeMenu");
        price=param*1.5;
    }
}

class DiscountedMenu extends Menu{
    @Override
    void setPrice(double param){
        System.out.println("I am the version of class DiscountedMenu");
        price=param*0.75;
    }
}

Above is a hierarchical example. There is a class Menu(parent class) which is inherited by two child classes, SurchargeMenu and DiscountedMenu. Here setPrice(double param) is the overridden method.

Note that, not only the name is same but also the signature matches to the version of the parent.

Checklist for method overriding

  • The signature of the method must be the same in both parent and child class.
  • The return type must also be the same for the method in both parent and child class.
  • The parent method cannot be private(In case the parent and child class lies in different packages, it cannot be default as well).
  • The parent method cannot be final.
  • The parent method cannot be static.

Let us continue with our above-stated example.

class Menu{
    double price;
    void setPrice(double param){
        System.out.println("I am the version of class Menu");
        price=param;
    }
}

class SurchargeMenu extends Menu{
    void setPrice(double param){
        System.out.println("I am the version of class SurchargeMenu");
        price=param*1.5;
    }
}

class DiscountedMenu extends Menu{
    void setPrice(double param){
        System.out.println("I am the version of class DiscountedMenu");
        price=param*0.75;
    }
}

public class ClassMain{
    public static void main(String args[]){
        Menu m = new Menu();
        m.setPrice(10); //Version of Menu
        System.out.println("The value of price after overridden method call is "+m.price);
        m = new SurchargeMenu(); 
        m.setPrice(10); //Version of Surcharge Menu
        System.out.println("The value of price after overridden method call is "+m.price);
        m = new DiscountedMenu();
        m.setPrice(10); //Version of Discounted Menu
        System.out.println("The value of price after overridden method call is "+m.price);
    }
}

Output:

I am the version of class Menu
The value of price after overridden method call is 10.0
I am the version of class SurchargeMenu
The value of price after overridden method call is 15.0
I am the version of class DiscountedMenu
The value of price after overridden method call is 7.5

Few inferences we can deduce from the above example are:

a) The parent class variable can hold the reference of its child classes. In the example, we can see that variable m can hold the references of its children classes.

b) It is the case of run-time polymorphism i.e late binding.  Line 26  is easy to understand. m holds the reference of class Menu, as a result, the setPrice() called in line 26 is actually class Menu version. We see the similar invocation of setPrice() at line 29 and 32. This is where run-time polymorphism comes into action. The version of setPrice() to be called depends on the reference which m is holding, which is constantly changing, hence dynamic.

As a result, the JVM identifies the object type which is being referred to, at the run time. This is called late binding.


super keyword

super is applicable whenever there exists IS-A relationship between 2 classes. This keyword is to be used inside the child class to refer to its immediate parent. It is frequent to encounter the same data members among parent and child class. To provide an extra layer of clarity, super is used. Its usage signifies that we are referring to the parent’s version. It can be used with variables, methods and constructors.

Use of super with variables

super when used with a variable in the child class, signify that we are referring to a variable of its immediate parent class.

Example:

class Menu{
    double price=250;
    void printPrice(){
     System.out.println("Price of menu is "+price);  
    }
}

class SurchargeMenu extends Menu{
    double price=400;
    void printPrice()
    {
     System.out.println("Price of Surcharge Menu is "+super.price);  //referring to price of class Menu
    }
}

public class ClassMain{
    public static void main(String args[]){
        Menu m = new SurchargeMenu();
        m.printPrice();
    }
}

Output:

Price of Surcharge Menu is 250.0

Use of super with methods

super when used with a method in the child class, signify that we are referring to a method of its immediate parent class.

Example:

class Menu{
    double price=250;
    void printPrice(){
     System.out.println("Price of Menu is "+price);  
    }
}

class SurchargeMenu extends Menu{
    double price=400;
    void printPrice(){
     super.printPrice();  //We are reffering to printPrice of class Menu
     System.out.println("Price of Surcharge Menu is "+price);  
    }
}

public class ClassMain{
    public static void main(String args[]){
        Menu m = new SurchargeMenu();
        m.printPrice();
    }
}

Output:

Price of Menu is 250.0
Price of Surcharge Menu is 400.0

Use of super with constructor

super when used inside the constructor of a child class, signify that we are invoking the constructor of the immediate parent class. We simply pass the arguments inside (), and the constructor version matching the set, gets invoked. By the convention of constructor chaining, the use of super should be in the first statement of constructor.

Example:

class Menu{
    double priceA;
    Menu(double param){
        priceA=param;
    }
}

class SurchargeMenu extends Menu{
    double priceB;
    SurchargeMenu(double param1, double param2){
        super(param1);  //This invokes the constructor of class Menu
        priceB=param2;
    }
}


public class ClassMain{
    public static void main(String args[]){
        SurchargeMenu m = new SurchargeMenu(10,20);
        System.out.println("Value of priceA is "+m.priceA);
        System.out.println("Value of priceB is "+m.priceB);
    }
}

Output:

Value of priceA is 10.0
Value of priceB is 20.0

This was an overview of Java Overriding and super keyword. In the next lecture, we will discuss static and dynamic polymorphism in detail.