Polymorphism in Java

  • Polymorphism in java, is an object’s ability to take on various forms. It enables us to complete one action in many different ways. Polymorphism is useful in carrying out inheritance processes in java.

  • An entity which behaves differently in different cases is called as polymorphism
    Example: You are using one button to switch the computer ON and OFF. This is the polymorphic behavior.

  • We can see polymorphism in java at compile time and runtime

  • Compile time polymorphism is also called method overloading and runtime polymorphism is called method overriding.


Method overloading is a feature that lets a class have multiple methods with the same name. Here, the method remains the same but the parameters are different.

  • Method Overloading exists in the same class. There is no need of a superclass and subclass relationship
  • Method name must be the same
  • Parameters must be different, the count of parameters does not matter
  • Parameters must be changed in one of the following three ways:
    • Types of parameters
    • Number of parameters
    • Order of parameters
  1. Access specifiers can be anything.
  2. Return type can be anything.
  3. Exceptions thrown can be anything.
  4. Implementation does not matter in this case as the method can have any kind of logic.
  5. Method overloading is not for changing any business logic but just for increasing the maintainability and readability of a code.

Here, our requirement is completed in both cases, and we will now consider clients.
In the first option, we will let client know changes in their existing method.
Then the client will say:

  • We don't required aadhar card, then why should we change our code?

  • Tomorrow, if any parameters are added, will you be asking us to change our code again?

  • We need to do a lot of testing for changes, our client may say - “Please do not change methods repeatedly but write other methods so that if we need, we will call separately.”

  • In the second option we will let the client know about the changes made in new method. They will then say:

  • Ok we will accommodate you but what changes you have made in your method?

  • Could you explain to us how it will be impacting our code?

  • If everything is same and only parameter is increased, why you not gave the same name of method so that we don’t get confused;
    so, we will face a lot of issues and queries from clients.

  • Tomorrow, if a new developer joins the company, he will not have any idea that both the methods are same except only parameters because the names are very different

Now, we will use third option by using overloading


Testing by client – if we run the below given program, what will be the output?

  • In Scenario VI, null can be accommodated in all m1 method but at the end, only one m1 method will get executed. That is, the more specific m1 will get called among A, B, C and in this case, C is more specific, and has more information out of all three classes.
    So output is -- I am in m1 C

Changes in Scenario

  • In this Scenario VII null can be accommodated in all m1, but at the end, only the m1 which is more specific will get called. Among A, B and C, C is more specific and has more information than the other two classes, so m1( ) of C gets called.

    Now D is nowhere related to all these classes. So we can't say which is more specific and therefore, the compiler gets confused and it will show a compile time error. So, there will be no output but compile time error that is shown is :
    "The method m1(null) is ambiguous for the type OverLoadingScenarios"

Consider the program shown below :

  • Why is method overloading called compile time polymorphism? As it has been identified at compile time, which method to call. Also, if we consider m1 as an entity, then it behaves differently at different times, and called compile time polymorphism.

The process of implementing superclass method in subclass is called method overriding. As per java principles, classes are closed for modification, so if we want to modify a method in any class, changing the existing method is not a good idea. Instead of that, we should extend that class and override method in child class.


  • In the above program, B is implementing the method m1() with the same signature as super class A i.e m1 () of class B is overriding m1() of class A

  • As per object oriented concept, the best practice is that class should not open for modification

  • If you want to add new functionality to existing class or if you want to modify the existing functionality of class, then you should not disturb the existing class [in this case class A m1(). You should always write the subclass of the existing class.]

  • The Subclass can be written for three reasons:

    • To add new features/properties [if student had properties age and location and in future we get requirement to add address for student, we should go for extending a class and adding a property in subclass as address]

    • To override/change the existing functionality

    • To inherit the existing functionality

  • When you are overriding superclass methods in subclass you need to follow certain rules

    • The subclass method name must be the same as superclass method name

    • Subclass method parameters must be the same as superclass method parameters [not even subclass as a parameter is allowed]

    • Subclass method's return type must be the same as superclass method return type [sub type is allowed]

    • Subclass method's access modifier must be the same or higher than the superclass method access modifier


  • superclass In subclass, we can have access specifier
    public Public
    protected Protected, Public
    default Default, Protected, Public
    private Not possible possible to Override

Dynamic Dispatch :
Dynamic dispatch is the process of assigning subclass object to superclass reference variable. A a=new B();
B is subclass and A is a superclass.

  • @Override is the annotation which is used to make compiler check all the rules of overriding
    If this annotation is not written, then the compiler will apply all overriding rules only if

    1. the superclass and subclass relation exist
    2. method name same
    3. the parameters of both methods are same.
  • But, if we write @Override on subclass method, then the compiler must apply all rules, irrespective of the above three points.
    Examples:
    Without annotation
    • In superclass
    • private void m1 () {
          System.out.println("XX-m1");
          }
    • In subclass
    • private void m1 () {        
          System.out.println("XX-m1");
          }
            // Compile time Ok
            // Runtime Ok 

    With annotation
    • In superclass
    • private void m1 () {
          System.out.println("XX-m1");
          }
    • In subclass
    •  @Override
          private void m1 () {       
              System.out.println("XX-m1");
          }
            // Compile time 
            //Error - private method cannot be overridden 
            
    Examples:
    Without annotation
    • In superclass
    • public static void m1 () { 
          System.out.println("XX-m1");
          }
    • In subclass
    • public static void m1 () {      
          System.out.println("YY-m1");
          }
            // Compile time
            //OK – it is not overriding as static method won't override 
            //Runtime OK 

    With annotation
    • In superclass
    • public static void m1 () { 
          System.out.println ("XX-m1");
          }
    • In subclass
    •  @Override
          public static void m1 () {      
              System.out.println ("YY-m1");
          }
            // Compile time 
            // Error - static method cannot be overridden 
            // Runtime Error 
            

  • When superclass method throws an exception then the subclass can do one of the following :
    1. Subclass methods can ignore the method level exception
    2. Subclass method can throw the same exception which is thrown by superclass method
    3. Subclass method can throw the exception which is the subclass of superclass method's exception
    4. Subclass method cannot throw the exception which is the superclass of superclass method's exception
    5. Subclass method cannot throw the exception which is non subclass to superclass method exception

    Example: If the superclass has
         void m1 () and throws B
    Then in this subclass we can have assume that A , B, C and D are exception classes as A ' B ' C ' D, A is top class and D is the last child in hierarchy.

    • void m1 ()
    • void m1 () throws B
    • void m1 () throws D
    • void m1 () throws A
    • void m1 () throws C

Without Annotation Example :
The assumption is that X is superclass and Y is subclass

  • In superclass
  •   public void m1 () {
        System.out.println("XX-m1");
        }
  • In subclass
  •   public void m1 () { 
        System.out.println("XX-m1");
        }
         // Compile time Ok
         // Runtime	Ok 
  • In superclass
  •   public void m1 () {
        System.out.println("XX-m1");
        }
  • In subclass
  •   void m1 () { 
        System.out.println("XX-m1");
        }
         // Compile time NOT OK - access specifiers must be bigger or same 
         // Runtime	NOT OK 
  • In subclass
  •   void m1 () { 
        System.out.println("XX-m1");
        }
         // Compile time NOT OK - access specifiers must be bigger or same 
         // Runtime	NOT OK 
  • In superclass
  •   public XX m1 (){
        System.out.println("XX-m1");
        }
  • In subclass
  •   public YY m1 () { 
        System.out.println("YY-m1"); 
        }
         // Compile time OK - If  XX  is a superclass and YY is a subclass 
         // Runtime OK
  • In superclass
  •   public void m1 (XX xx) { 
        System.out.println("XX-m1"); 
        }
  • In subclass
  •   public void m1 (YY yy) {
        System.out.println("YY-m1");
        }
         // Compile time parameters must be same
         Runtime OK
  • In superclass
  •   private void m1 () {
        System.out.println("XX-m1");
        }
  • In subclass
  •   private void m1 () { 
        System.out.println("YY-m1");
        }
        // Compile time OK - it's not overriding, it's just an independent method 
        // Runtime OK - It will run independently
    
  • In superclass
  •   static void m1 () {
        System.out.println("XX-m1");
        }
  • In subclass
  •   static void m1 () { 
        System.out.println("YY-m1");
        }
        // Compile time OK
        // It's not overriding, it's just an independent method as
        // static method can never be overridden
        // Runtime OK 
    
  • In the industry, people use Overriding to change the behaviour of methods like below:
  • Class A{
            void m1() {
                //MD5 encryption algorithm
            }
          }

    In this class, we have the method m1 which has md5 encryption implemented. Now, when our requirement changes, we need to use the RSA algorithm

  • Option 1
  • class A{
            void m1(){
                //RSA encryption algorithm
            }
          }
  • Option 2
  • class B extends A { 
            void m1(){
                //RSA encryption algorithm
            }
          }

    If you observe option 2 above, it is more feasible when we use overriding. Because if the requirement arises again which says to go for MD5 algorithm, we can just comment

  • Option 2
  • class B extends A {
            /*
              void m1(){
                //RSA encryption algorithm
            }
            */
           }
        

    Client will be unaffected as they can call

        B a=new B();
        a.m1(); 

So, if you have method in B it will be called, or if there is not there then superclass method will be called. So we will have more flexibility, which we should always ensure for future maintenance issues.

[In the real sense it is advisable not to disturb clients repeatedly. Do coding in such a way that even after changes are made, our code is easily extensible. Like here, we just need to comment and uncomment for MD5 and RSA according to requirement]

@Override is just for readability. I mean, if the developer has left the company and a new developer joins, then he/she will easily understand that this method is overridden so that he can further modify easily in future. At least he/she will understand that there is some relationship between the two methods.

  • Q. Can I override private method?
    A: No, you cannot override private methods because when superclass method is private it will not be visible to subclass. Whatever the methods you write in subclass will be treated as new method and not an overridden method.

  • Q. Can I override a static method?
    A: No.

  • Q. Can I stop method overriding?
    A: Yes, by declaring method as final.

  • Q. Can I stop inheritance?
    A: Yes, by declaring super class as final.

  • Q. Why is method overriding called runtime polymorphism?
    1. We have class A with m1- "A m1" prints
    2. We have subclass B with m1 - "B m1" prints
    3. Client wrote main method and in main,
      1. A a=new B;
      2. a.m1();
      3. The above written line is compiled as class A has m1();
      4. At runtime, we will get output of m1 which, is there at B
      5. Since at compile time we think m1 of A will get called but m1 of B got executed instead. This is called at runtime polymorphism