Polymorphism in Java
Polymorphism is the ability for the same behavior to have multiple different manifestations or forms.
Polymorphism is the same interface being used with different instances to perform different operations, as shown in the figure:
Polymorphism is the manifestation of multiple forms of an object.
In real life, for example, the action of pressing the F1 key:
- If you are in the Flash interface, the AS 3 help document pops up;
- If you are in Word, the Word help pops up;
- If you are in Windows, the Windows Help and Support pops up.
The same event occurring on different objects produces different results.
Advantages of Polymorphism
- Eliminates coupling between types
- Substitutability
- Extensibility
- Interfaceability
- Flexibility
- Simplicity
Three Necessary Conditions for Polymorphism
- Inheritance
- Overriding
- Parent class reference pointing to child class object:
Parent p = new Child();
class Shape { void draw() {} } class Circle extends Shape { void draw() { System.out.println("Circle.draw()"); } } class Square extends Shape { void draw() { System.out.println("Square.draw()"); } } class Triangle extends Shape { void draw() { System.out.println("Triangle.draw()"); } }
When using polymorphism to call methods, it first checks if the method exists in the parent class. If not, a compile error occurs; if it does, it then calls the method of the subclass.
The benefits of polymorphism: It allows for good program extensibility and can handle all class objects generically.
Below is a demonstration of a polymorphism example, detailed explanations can be found in the comments:
Test.java File Code:
public class Test {
public static void main(String[] args) {
show(new Cat()); // Call show method with Cat object
show(new Dog()); // Call show method with Dog object
Animal a = new Cat(); // Upcasting
a.eat(); // Calls Cat's eat
Cat c = (Cat)a; // Downcasting
c.work(); // Calls Cat's work
}
public static void show(Animal a) {
a.eat();
// Type checking
if (a instanceof Cat) { // Cat's actions
Cat c = (Cat)a;
c.work();
} else if (a instanceof Dog) { // Dog's actions
Dog c = (Dog)a;
c.work();
}
}
}
abstract class Animal {
abstract void eat();
}
class Cat extends Animal {
public void eat() {
System.out.println("Eating fish");
}
public void work() {
System.out.println("Catching mice");
}
}
class Dog extends Animal {
public void eat() {
System.out.println("Eating bones");
}
public void work() {
System.out.println("Guarding the house");
}
}
Executing the above program, the output is:
Eating fish
Catching mice
Eating bones
Guarding the house
Eating fish
Catching mice
Virtual Function
The existence of virtual functions is for polymorphism.
Java does not have the concept of virtual functions; its regular functions are equivalent to C++'s virtual functions, with dynamic binding being Java's default behavior. If you do not want a function in Java to have virtual function characteristics, you can add the final keyword to make it a non-virtual function. We will introduce how the behavior of overridden methods in Java affects polymorphism when designing classes.
We have discussed method overriding, which means that a subclass can override a method of its superclass.
When an overridden method is called by a subclass object, the method of the subclass is invoked, not the overridden method in the superclass.
To call the overridden method in the superclass, the keyword super must be used.
Employee.java File Code:
/* File name : Employee.java */
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Employee Constructor");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Mailing a check to " + this.name
+ " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
Suppose the following class inherits from the Employee class:
Salary.java File Code:
/* File name : Salary.java */
public class Salary extends Employee
{
private double salary; // Annual salary
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Within mailCheck of Salary class ");
System.out.println("Mailing check to " + getName()
+ " with salary " + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("Calculating salary pay for " + getName());
return salary/52;
}
}
Now, let's carefully read the following code and try to give its output:
VirtualDemo.java File Code:
/* File name : VirtualDemo.java */
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("Employee A", "Beijing", 3, 3600.00);
Employee e = new Salary("Employee B", "Shanghai", 2, 2400.00);
System.out.println("Calling mailCheck using Salary reference -- ");
s.mailCheck();
System.out.println("\nUsing Employee reference to call mailCheck--");
e.mailCheck();
}
}
The above example compiles and runs as follows:
Employee constructor
Employee constructor
Using Salary reference to call mailCheck --
mailCheck method of Salary class
Mailing check to: Employee A, salary: 3600.0
Using Employee reference to call mailCheck--
mailCheck method of Salary class
Mailing check to: Employee B, salary: 2400.0
Example Analysis
-
In the example, two Salary objects are instantiated: one using a Salary reference s, and the other using an Employee reference e.
-
When calling s.mailCheck(), the compiler finds mailCheck() in the Salary class at compile time, and the JVM calls the mailCheck() method of the Salary class during execution.
-
e is a reference to Employee, but the reference e ultimately runs the mailCheck() method of the Salary class.
-
At compile time, the compiler uses the mailCheck() method in the Employee class to verify the statement, but during runtime, the Java Virtual Machine (JVM) calls the mailCheck() method of the Salary class.
This entire process is known as virtual method invocation, and the method is called a virtual method.
All methods in Java can behave in this way, so overridden methods can be called at runtime, regardless of the data type of the reference variable in the source code at compile time.
Polymorphism Implementation Methods
Method One: Overriding:
Method Two: Interfaces
-
- The most representative interface in real life is the socket, such as a three-pronged plug that can be plugged into a three-hole socket, because this is a standardized interface rule in each country. It may not work abroad because of the different defined interface types there.
-
- Interfaces in Java are similar to real-life interfaces, being a collection of method signatures without method implementations. For more details, see the Java Interfaces section.
Method Three: Abstract Classes and Abstract Methods
For more details, see the Java Abstract Classes section.