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.
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
}
}
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
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);
}
}
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.
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 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
private void m1 () {
System.out.println("XX-m1");
}
private void m1 () {
System.out.println("XX-m1");
}
// Compile time Ok
// Runtime Ok
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
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
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
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.