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
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
package com.javabykiran.Poly;
public class Arithmatic {
void sum(int a) {
System.out.println(a + a);
}
void sum(double a) {
System.out.println(a + a);
}
void sum(double a, double b) {
System.out.println(a + b);
}
int sum(int a, int b) {
return a + b;
}
void sum(int a, double b) {
System.out.println(a + b);
}
void sum(double a, int b) {
System.out.println(a + b);
}
}
package com.javabykiran.Poly;
public class Lab1 {
public static void main(String[] args) {
Arithmatic ar = new Arithmatic();
ar.sum(10); // 20
ar.sum(10.5); // 21.0
ar.sum(10.5, 20.5); // 31.0
ar.sum(10, 20.9); // 30.9
ar.sum(10.5, 20); // 30.5
ar.sum(10,10); // 20
}
}
- Access specifiers can be anything.
- Return type can be anything.
- Exceptions thrown can be anything.
- Implementation does not matter in this case as the method can have any kind of logic.
- Method overloading is not for changing any business logic but just for increasing the maintainability and readability of a code.
When should we use Overloading ? Imagine this class we have created in 2005
class A{
public void insertStudent (int age,String loc,int phoneNo){
}
}
//Clients are calling this class as below:
Client 1:
A a =new A();
a.insertStudent(20,"pune",8888809416);
Client 2:
A a1 =new A();
a1.insertStudent(20,"nagpur",8888809416);
Now in 2010, we have a requirement of capturing the aadhar card of a student.
We have two options:
Option 1
class A {
// increasing paramenters in same method
public void insertStudent(int age, String loc,int phoneNo, int aadharCardNo){
}
}
Option 2
class A {
// write another method with a different name
public void AadharCard (int age, String loc, int phone No, int aadharCardNo){
}
public void insertStudent(int age, String loc,int phoneNo){
} //old existing method
}
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
public class A {
}
public class B extends A {
}
public class C extends B {
}
public class OverLoadingScenarios {
void m1(A a) {
System.out.println("I am in m1 A");
}
void m1(B b) {
System.out.println("I am in m1 B");
}
void m1(C c) {
System.out.println("I am in m1 C");
}
Testing by client – if we run the below given program, what will be the output?
package com.javabykiran.Poly;
public class OverLoadingTest {
public static void main(String[] args) {
OverLoadingScenarios loadingScenarios = new OverLoadingScenarios();
// Scenario I
A a=new A();
loadingScenarios.m1(a);
// Scenario II
B b=new B();
loadingScenarios.m1(b);
// Scenario III
C c=new C();
loadingScenarios.m1(c);
// Scenario IV
B bc=new C();
loadingScenarios.m1(bc);
// Scenario V
A ab=new B();
loadingScenarios.m1(ab);
// Scenario VI
loadingScenarios.m1(null);
}
}
Output:
I am in m1 A
I am in m1 B
I am in m1 C
I am in m1 B
I am in m1 A
I am in m1 C
- 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
public class OverLoadingScenarios {
void m1(A a) {
System.out.println("I am in m1 A");
}
void m1(B b) {
System.out.println("I am in m1 B");
}
void m1(C c) {
System.out.println("I am in m1 C");
}
void m1(D d) { //D is not related to A,B and C Hierarchy
System.out.println("I am in m1 D");
}
}
public static void main(String[] args) {
OverLoadingScenarios ls =new OverLoadingScenarios();
// Scenario VII
ls.m1(null);
}
}
-
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 :
package com.javabykiran.Poly;
public class X {
void m1(Object object) {
System.out.println("m1 with object as a param");
}
void m1(String string) {
System.out.println("m1 with string as a param");
}
public static void main(String[] args){
X x = new X();
x.m1(null);
x.m1(new Object());
x.m1("Kiran Sir");
x.m1(new X());
x.m1(new string());
}
}
Output:
m1 with string as a param
m1 with object as a param
m1 with string as a param
m1 with object as a param
m1 with string as a param
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.
Method Overriding
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.
public class A {
public void m1() {
System.out.println("A-m1");
}
}
public class B extends A {
public void m1() {
System.out.println("B-m1");
}
public void m3() {
System.out.println("B-m3");
}
}
public class Lab2S {
public static void main(String args[]){
B obj =new B ();
obj.m1 ();
obj m3 ();
}
}
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 annotation
@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- the superclass and subclass relation exist
- method name same
- 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.- In superclass
- In subclass
- In superclass
- In subclass
- In superclass
- In subclass
- In superclass
- In subclass
- When superclass method throws an exception then the subclass can do one of the following :
- Subclass methods can ignore the method level exception
- Subclass method can throw the same exception which is thrown by superclass method
- Subclass method can throw the exception which is the subclass of superclass method's exception
- Subclass method cannot throw the exception which is the superclass of superclass method's exception
- Subclass method cannot throw the exception which is non subclass to superclass method exception
- void m1 ()
- void m1 () throws B
- void m1 () throws D
- void m1 () throws A
- void m1 () throws C
Without annotation
private void m1 () {
System.out.println("XX-m1");
}
private void m1 () {
System.out.println("XX-m1");
}
// Compile time Ok
// Runtime Ok
With annotation
private void m1 () {
System.out.println("XX-m1");
}
@Override
private void m1 () {
System.out.println("XX-m1");
}
// Compile time
//Error - private method cannot be overridden
Without annotation
public static void m1 () {
System.out.println("XX-m1");
}
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
public static void m1 () {
System.out.println ("XX-m1");
}
@Override
public static void m1 () {
System.out.println ("YY-m1");
}
// Compile time
// Error - static method cannot be overridden
// Runtime Error
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.
Without Annotation Example :
The assumption is that X is superclass and Y is subclass
- In superclass
public void m1 () {
System.out.println("XX-m1");
}
public void m1 () {
System.out.println("XX-m1");
}
// Compile time Ok
// Runtime Ok
public void m1 () {
System.out.println("XX-m1");
}
void m1 () {
System.out.println("XX-m1");
}
// Compile time NOT OK - access specifiers must be bigger or same
// Runtime NOT OK
void m1 () {
System.out.println("XX-m1");
}
// Compile time NOT OK - access specifiers must be bigger or same
// Runtime NOT OK
public XX m1 (){
System.out.println("XX-m1");
}
public YY m1 () {
System.out.println("YY-m1");
}
// Compile time OK - If XX is a superclass and YY is a subclass
// Runtime OK
public void m1 (XX xx) {
System.out.println("XX-m1");
}
public void m1 (YY yy) {
System.out.println("YY-m1");
}
// Compile time parameters must be same
Runtime OK
private void m1 () {
System.out.println("XX-m1");
}
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
static void m1 () {
System.out.println("XX-m1");
}
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
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
class A{
void m1(){
//RSA encryption algorithm
}
}
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
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?
- We have class A with m1- "A m1" prints
- We have subclass B with m1 - "B m1" prints
- Client wrote main method and in main,
- A a=new B;
- a.m1();
- The above written line is compiled as class A has m1();
- At runtime, we will get output of m1 which, is there at B
- 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