Polymorphism
What is polymorphism in JAVA? How is that achieved?
In JAVA, if any object passes more than one IS A relation, then the object is said to be polymorphic. Any object apart from type Object is polymorphic because it passes at least 2 IS A relations, 1 for its own type and 2 for class Object.
Remember that the only way to access an object is through a reference variable, and there are a few key things to remember about references:
■ A reference variable can be of only one type, and once declared, that type can never be changed (although the object it references can change).
■ A reference is a variable, so it can be reassigned to other objects, (unless the reference is declared final).
■ A reference variable's type determines the methods that can be invoked on the object the variable is referencing.
■ A reference variable can refer to any object of the same type as the declared reference, or—this is the big one—it can refer to any subtype of the declared type!
■ A reference variable can be declared as a class type or an interface type. If the variable is declared as an interface type, it can reference any object of any class that implements the interface.
In JAVA, polymorphism is achieved via Method Overloading (Compile Time Polymorphism) & Method Overriding (Runtime Polymorphism).
What is method overloading? Explain with examples.
Overloaded methods let you reuse the same method name in a class, but with different arguments (and optionally, a different return type).
A method can be overloaded within the same class or in the inherited class.
An important point to remember: The list of arguments is the deciding factor as to which overloaded method will be invoked.
Example 1 : Method overload within same class
class Add{
static int add(int a,int b){return a+b;}
static int add(int a,int b,int c){return a+b+c;}
}
class TestOverloading{
public static void main(String[] args){
System.out.println(Add.add(11,11));
System.out.println(Add.add(11,11,11));
}
}
Output
22
33
Example 2 : Method overload between parent & child class
class Base { public int f(int i) { System.out.print("f (int): "); return i+3; } } class Derived extends Base { public double f(double i) { System.out.print("f (double) : "); return i + 3.3; } } class myprogram3 { public static void main(String args[]) { Derived obj = new Derived(); System.out.println(obj.f(3)); System.out.println(obj.f(3.3)); } } |
Output
f (int): 6
f (double): 6.6
Why is method overloading also called compile time polymorphism?
Let’s review the below piece of code.
class Animal {
}
class Horse extends Animal {
}
class UseAnimal {
public void doSomething(Animal a)
{
System.out.println("In Animal");
}
public void doSomething(Horse h) //doSomething is overloaded
{
System.out.println("In horse");
}
}
public class Test {
public static void main(String[] args) {
UseAnimal UA1 = new UseAnimal();
Animal a1 = new Horse();
Horse h1 = new Horse();
UA1.doSomething(a1);
}
}
Output: In Animal
Explanation:
In the above example, we have created a Horse object using the Animal reference variable. When we pass that reference to the doSomething() method, because a1 refers to a Horse object, it is tempting to say it will invoke the doSomething() method which takes a Horse object as an argument; but that is not true. It will invoke the doSomething() which takes Animal object, because Method Overloading does happen on compile time.
What is method overriding? Explain with examples.
Whenever a class inherits methods from a superclass, we can override the method. In Method Overriding, we have a huge benefit of implementing the method specific to the child class.
But if a method is declared as Final in super class, that cannot be overridden.
Example –
public class Animal {
public void eat() {
System.out.println("Generic Animal Eating Generically");
}
}
class Horse extends Animal {
public void eat() {
System.out.println("Horse eating hay, oats and horse treats");
}
}
public class Test{
public static void main(String[] args) {
Horse h1 = new Horse();
h1.eat();
}
}
Output
Horse eating hay, oats and horse treats
Explanation-
In the above program, eat() method is overridden in Horse class. So when we call the eat() method on a horse object, the implementation of eat() in Horse class is executed.
What is Dynamic Method Invocation in JAVA? Or, Why method overriding is known as runtime polymorphism?
Dynamic Method Invocation is the process of determining which version of an overridden method to use based on the actual object (not the reference type) at runtime. That is why method overriding is also referred to as runtime polymorphism.
In the below example, though the reference type is of Animal, the actual object is Horse type object. So, on invoking the eat() method on reference type b, Horse specific eat() method will be invoked, not the eat() method from Animal.
class Animal {
public void eat()
{
System.out.println("Animal eating");
}
}
class Horse extends Animal{
public void eat()
{
System.out.println("Horse eating");
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal ();
a.eat();
Animal b = new Horse();
b.eat();
Horse h = new Horse();
h.eat();
}
}
Output
Animal eating
Horse eating
Horse eating
How does polymorphism work together with overloading and overriding?
How does polymorphism work with overloaded methods? From what we just looked at, it doesn't appear that polymorphism matters when a method is overloaded. If you pass an Animal reference, the overloaded method that takes an Animal will be invoked, even if the actual object passed is a Horse. Once the Horse masquerading as Animal gets into the method, however, the Horse object is still a Horse despite being passed into a method expecting an Animal. So it's true that polymorphism doesn't determine which overloaded version is called; polymorphism does come into play when the decision is about which overridden version of a method is called. But sometimes, a method is both overloaded and overridden. Imagine the Animal and Horse classes look like this:
public class Animal {
public void eat() {
System.out.println("Generic Animal Eating Generically");
}
}
public class Horse extends Animal {
public void eat() {
System.out.println("Horse eating hay ");
}
public void eat(String s) {
System.out.println("Horse eating " + s);
}
}
Notice that the Horse class has both overloaded and overridden the eat() method.
What is reference variable casting?
Reference variable casting is assigning a superclass (or Interface) variable to a subclass object or vice versa. Casting is of 2 types, Upcasting & Downcasting.
Upcasting Example
class Animal{
public void eat()
{
System.out.println("Animal eating");
}
}
class Dog extends Animal{
public void eat()
{
System.out.println("Dog eating");
}
public void makeNoise()
{
System.out.println("Dog barking");
}
}
public class Upcasting {
public static void main(String[] args) {
Animal a = new Dog();//Up casting
a.eat(); /*
Compiles fine because compiler sees that Animal class has eat() method, does not complain.But when runs, takes Dog class eat() method*/
((Dog)a).makeNoise();
/*
* Does not compile until we tell the compiler that Object a is really a Dog's object. So we need to cast it to Dog. */
}
Downcasting Example
class Animal{
public void eat()
{
System.out.println("Animal eating");
}
}
class Dog extends Animal{
public void eat()
{
System.out.println("Dog eating");
}
public void makeNoise()
{
System.out.println("Dog barking");
}
}
public class Downcasting {
public static void main(String[] args) {
Animal a = new Animal
((Dog)a).makeNoise(); //Downcasting
/*
* Does not compile until we tell the compiler that Object a is really a Dog's object. So we need to cast it to * Dog.*/
}
}
The difference in the above two examples is the actual objects which are created. In the first example, we have created a Dog object, for which calling the makeNoise() method is absolutely fine. Because, the makeNoise() method overrides and JVM looks for the original object (not the reference type) while working with Overriding. So when it finds that it’s really a Dog object, the output is provided.
But in the second example, we are creating an Animal object. Then we have down casted to Dog type. This down casting is fine until the program is in compilation mode, because the compiler does not complain thinking that after downcast, ‘a’ becomes a Dog object. But while JVM runs the program, it finds that ‘a’ is never a Dog object and throws a classcast exception.
To avoid this problem, we can use an instance of operator to make sure that ‘a’ is really a dog object. Here is the improved version of the code:
class Animal{
public void eat()
{
System.out.println("Animal eating");
}
}
class Dog extends Animal{
public void eat()
{
System.out.println("Dog eating");
}
public void makeNoise()
{
System.out.println("Dog barking");
}
}
public class ObjType {
public static void main(String[] args) {
Animal [] a ={new Animal, new Dog, new Animal}
For(Animal a1:a)
{
if(a1 instanceof Dog)
((Dog)a1).makeNoise();
}
Rules for method overloading and overriding
What are the different access modifiers are there in JAVA?
The access modifiers in Java specifies the accessibility or scope of a field, method, constructor, or class. We can change the access level of fields, constructors, methods, and class by applying the access modifier on it.
Let’s understand the access level of each access modifier using a single table.
What is the usage of ‘super’ and ‘this’ keywords?
The super keyword in java is used to refer immediate parent class object. Whenever we create the object of a subclass, an instance of the parent class is created implicitly and members of the parent instance can be accessed via super keyword.
Usage of Java super Keyword
- super is used to refer to the immediate parent class instance.
- variable.super is used to invoke immediate parent class.
- constructor.super is used to invoke immediate parent class method.
Example 1: Access parent class instance variable
class Vehicle{
int speed=50;
}
class Bike extends Vehicle{
int speed=100;
void display(){
System.out.println(super.speed);
}
public static void main(String args[]){
Bike b=new Bike();
b.display();
}
}
Output
50 // Superclass instance variable is accessed in child class using super keyword.
Example 2: Access parent class constructor
class Vehicle{
Vehicle(){System.out.println("Vehicle is created");}
}
class Bike extends Vehicle{
Bike(){
super();//will invoke parent class constructor
System.out.println("Bike is created");
}
public static void main(String args[]){
Bike b=new Bike();
}
}
Output
Vehicle is created
Bike is created
*** This call to super class constructor is implicit, meaning if we do not include the call; still, it will execute super class constructor before it executes the current class’s constructor.
Example 3: Access parent class method
class Person{
void message(){System.out.println("welcome");}
}
class Student extends Person{
void message(){System.out.println("welcome to java");}
void display(){
message();//will invoke current class message() method
super.message();//will invoke parent class message() method
}
public static void main(String args[]){
Student s=new Student();
s.display();
}
}
Output
welcome to java
welcome
Usage of java this Keyword
- this is used to refer to the current class instance variable.
- this is used to refer to the current class constructor.
- this is used to refer to the current class method.
- pass current class object reference to a method
Example 1: Access current class instance variable
private String javaFAQ;
void methodName(String javaFAQ) {
this.javaFAQ = javaFAQ; //this refers to instance variable javaFAQ
}
Example 2: Access current class constructor
public class Keywords {
public Keywords(String s)
{
System.out.println("String type args");
}
public Keywords(int i)
{
this("str");// the second constructor is calling the first one, using proper argument.
System.out.println("int type args");
}
public static void main(String[] args) {
new Keywords(5);
}
}
Output
String type args
int type args
Example 3: Access current class method
public class Keywords {
public void m()
{
System.out.println("In first method");
}
public void n()
{
this.m(); //It calls m()
}
public void p()
{
this.n();//It calls n()
}
public static void main(String[] args) {
(new Keywords()).p();//Final call to m(), sequentially towards top.
}
}
Output
In first method
Example 4: pass current class object reference to a method
public class Keywords {
public void m(Keywords ks)
{
System.out.println("Passing object reference");
}
public void p()
{
this.m(this); //Passing current class object to method using this keyword
}
public static void main(String[] args) {
(new Keywords()).p();
}
}
Output
Passing object reference
With this article, we have Java OOPs series. Let me know your thoughts and feedbacks in the comment section.