Monday 22 May 2017

Polymorphism

Polymorphism is an important concept of OOPs. Every java developer should have sound knowledge of polymorphism. This post will discuss in detail the concept of polymorphism in Java.


1. What is polymorphism?

Answer:  "Polymorphism is ability to take more than one form as per definition". Let's discuss the meaning of each term from above mentioned definition. 
Definition says It's ability to take more than one form. Before we explore more on this let's first try to identify who takes more than one form. The answer is methods only because Java doesn't support operator level polymorphism.
It's clear that method takes more than one form. It means same method can do different task for us, but here point to note that is when we say same method it means same name only.
So we can say that we can achieve polymorphism in Java by creating different method to do different task while keeping name of method same provided signature (number, types and order of parameter) is different.

2. How many types of polymorphism does Java support?

AnswerJava supports two kind of polymorphism:
1. Compile time or Static polymorphism: This is the type of polymorphism in which Java compiler knows at compile time that which method need to invoke depends on input parameters.
Java supports compile time polymorphism in form of Method overloading.
2. Run time or dynamic polymorphism: This is the type of polymorphism in which Java compiler can not say at compile time that which method need to invoke depends on input parameters. Instead Java decides at run time depends on type of object. Java supports run time polymorphism in form of Method Overriding.

3. What is Method overloading?

Answer: As we discussed in last question method overloading is compile time or static polymorphism.
"Method overloading is having more than one method with same name but with different signature(type of input parameters, order of input parameter and number of input parameter)".
As per the above statement, We can create multiple methods with same name within a class provided each method has different signature.
There are three ways in which we can achieve method overloading in Java:
1. Based on number of input parameters,
2. Based on type of input parameters,
3. Based on oder of input parameters.
Example:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class TestOverLoading {
 public static void main(String[] args) {
  Calculator calculator = new Calculator();
  
  calculator.add(2, 5); //method with two parameter will be invoked
  
  calculator.add(2, 5, 6); //method with three parameter will be invoked
 }
}

class Calculator{
 int add(int a, int b){
  return a + b;
 }
 
 int add(int a, int b, int c){
  return a + b + c;
 }
}

Note : Java doesn't support overloading based on return type of method. Example: Below code will give compilation error as we can do method overloading based on return type.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class TestOverLoading {
 public static void main(String[] args) {
  Calculator calculator = new Calculator();
  
  calculator.add(2, 5);
  
 }
}

class Calculator{
 int add(int a, int b){ 
  return a + b;
 }
 
 float add(int a, int b){
  return a + b;
 }
 
}

Follow-up question: What will be output of below code, Whether it will give compile time error and if not which method will be invoked if any?


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class TestOverLoading {
 public static void main(String[] args) {
  Calculator calculator = new Calculator();
  
  calculator.add(2, 5); //Method with two parameter will be called
  
  calculator.add(2, 5, 6); //Java  will perform automatic up-casting in this case
 }
}

class Calculator{
 int add(int a, int b){
  return a + b;
 }
 
 double add(double a, double b, double c){
  return a + b + c;
 }
}
Explanation: In above code we have two overloaded methods one takes two integer parameter as input another takes three double parameter as input. At line number 7 we are invoking method add() with three parameters of integer type and there is no such method in Calculator class which matches with the input parameters. And here type casting of Java comes into picture. As per rule of type casting double can hold integer value hence compiler will pass the request to method which has the parameters type as double. So In this case method at line number 16 will be invoked.

So we can say that whenever compiler fails to find perfect match for a method call with given input parameters. Compiler looks for other overloaded method to find any of them can handle the input parameters as per Java type casting rule. If it finds the method then compiler performs automatic type casting and invokes the methods at run time.
Few examples like integer can be handled by long and double, float can be handled by double etc.

4. What is method overriding? 

AnswerAs we discussed in this post that method overriding is run time or dynamic polymorphism.
Example: In below example there are two sub class SavingAccount and CurrentAccount which extends Account class. We have three version of deposit() method which are defined in Account, SavingAccount and CurrentAccount respectively.
Method overriding is a way of providing your own implementation of method in sub class which declared/defined in parents class . As name suggests we over writes the method provided in parent class inside our child class.
Overriding concepts go hand in hand with Inheritance.

Can compiler tells at line number 30 which version of deposit() method will be invoked? Compiler can not say at compile time, because it depends on the type of object that we pass at run time. In the below code(from line 12-27) we will have the object depends on user's input at run time and type of that object will decide from which version of deposit() method will be invoked.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
import java.util.Scanner;

import javax.swing.text.DefaultEditorKit.DefaultKeyTypedAction;

public class TestOverridng {

 public static void main(String[] args) {
  final int amount = 5000;
  String accountTypeSelected = null;
  Account account = null;
  
  Scanner sc=new Scanner(System.in);
  System.out.println("Please provide account type\n");
  accountTypeSelected=sc.nextLine();

  switch(accountTypeSelected){
   case "Account" :
    account = new Account();
    break;
   case "Saving" :
    account = new SavingAccount();
    break;
   case "Current" :
    account = new CurrentAccount();
    break;
   default :
    account = new Account();
  }
  
  account.deposit(amount); //Now which deposit will be called it depends on user's choice.
    
 }

}

class Account{
 void deposit(int amount){
  System.out.println("Account's deposit called");
 }
}

class SavingAccount extends Account{
 @Override
 void deposit(int amount){
  System.out.println("Saving Account's deposit called");
 }
}

class CurrentAccount extends Account{
 @Override
 void deposit(int amount){
  System.out.println("Current Account's deposit called");
 }
}

Follow-up questions: What are different rules of overriding ?

Answer
1. We can't change name of method while overriding.
2. We need to keep input parameters same. There should be no change in number or type or order of input parameters. If we make any changes in input parameters then it will be a new method, it won't be a overrided version of parent's method. In this case in our child class we will get both the methods one from parent class and one from your child class. Actually it will be method overloading.
3. We can change return type of override method but that should be co-variant type.
4. Exception handling rules for overriding are as follows:
For Checked exceptions : If a method in parent class throws an exception and if we overrides the same method in our child class, then in this case from overrided method (child class) we can throw only those exceptions which are equal or narrower to the exception thrown from original method(parent class)

For Unchecked exceptions : For Unchecked or run-time exception you can throw any exception from child class, there is no limitation in this case. Since unchecked exception can not be detect at compile time.


If you will try to compile below code you will get compilation error at line 16 saying "Exception is not compatible with throws clause in Account.deposit(int)" and at the same time there is no compilation error at line 9. 
Since FileNotFoundException is a child class of IOException i.e. FileNotFoundException is not wider(general) than IOException, Which is according to rule of checked exception as we discussed above hence no compilation errors at line 9.
But Exception is super class of IOException. i.e Exception is more wider(general) than IOException which violates the rule of checked exception hence compilation error is there at line 16.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
class Account{
 void deposit(int amount) throws IOException{
  System.out.println("Account's deposit called");
 }
}

class SavingAccount extends Account{
 @Override
 void deposit(int amount) throws FileNotFoundException{
  System.out.println("Saving Account's deposit called");
 }
}

class CurrentAccount extends Account{
 @Override
 void deposit(int amount) throws Exception{   //It gives Compilation error
  System.out.println("Current Account's deposit called");
 }
}

Follow-up question : As mentioned above why there is a limitation for checked exception in case of method overriding?

Answer: Checked exceptions or those exceptions about which compiler knows at compile time and whenever we call a method which throws a checked exception then compiler ask caller either catch the exception or throw it up in call hierarchy. In case of overriding if we are calling a method then at the compile time compiler has no idea which method going to invoke it can only decides at run time whether parent or child it depends on type off object.
Now assume in above code if child class throws more general (wider) exception then compiler won't be able to guard because we are using reference of parent type that's why compiler will only able to check for exceptions thrown from parent's methods and will ignore exception thrown from child class. So in order to avoid this ambiguity or confusion Java designers decided to put this limitation.

No comments:

Post a Comment