Java - Polimorfismo
Polimorfismo é a capacidade de um objeto assumir muitas formas. O uso mais comum de polimorfismo em OOP ocorre quando uma referência de classe pai é usada para se referir a um objeto de classe filho.
Qualquer objeto Java que possa passar em mais de um teste IS-A é considerado polimórfico. Em Java, todos os objetos Java são polimórficos, pois qualquer objeto passará no teste IS-A para seu próprio tipo e para a classe Object.
É importante saber que a única maneira possível de acessar um objeto é através de uma variável de referência. Uma variável de referência pode ser de apenas um tipo. Uma vez declarado, o tipo de uma variável de referência não pode ser alterado.
A variável de referência pode ser reatribuída a outros objetos desde que não seja declarada final. O tipo da variável de referência determinaria os métodos que ela pode invocar no objeto.
Uma variável de referência pode se referir a qualquer objeto de seu tipo declarado ou a qualquer subtipo de seu tipo declarado. Uma variável de referência pode ser declarada como uma classe ou tipo de interface.
Exemplo
Vamos ver um exemplo.
public interface Vegetarian{}
public class Animal{}
public class Deer extends Animal implements Vegetarian{}
Agora, a classe Deer é considerada polimórfica, pois possui herança múltipla. A seguir são verdadeiros para os exemplos acima -
- Um cervo é um animal
- Um cervo é um vegetariano
- Um cervo é um cervo
- Um cervo é um objeto
Quando aplicamos os fatos da variável de referência a uma referência de objeto Deer, as seguintes declarações são legais −
Exemplo
Deer d = new Deer(); Animal a = d; Vegetarian v = d; Object o = d;
Todas as variáveis de referência d, a, v, o referem-se ao mesmo objeto Deer no heap.
Métodos Virtuais
Nesta seção, mostrarei como o comportamento de métodos sobrescritos em Java permite que você aproveite o polimorfismo ao projetar suas classes.
Já discutimos a substituição de métodos, onde uma classe filha pode substituir um método em seu pai. Um método substituído está essencialmente oculto na classe pai e não é invocado a menos que a classe filha use a palavra-chave super dentro do método de substituição.
Exemplo
/* 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("Constructing an Employee");
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;
}
}
Agora suponha que estendemos a classe Employee da seguinte forma -
/* 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("Computing salary pay for " + getName());
return salary/52;
}
}
Agora, você estuda o seguinte programa cuidadosamente e tenta determinar sua saída -
/* File name : VirtualDemo.java */
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("Mohd Mohtashim", "Ambehta, UP", 3, 3600.00);
Employee e = new Salary("John Adams", "Boston, MA", 2, 2400.00);
System.out.println("Call mailCheck using Salary reference --");
s.mailCheck();
System.out.println("\n Call mailCheck using Employee reference--");
e.mailCheck();
}
}
Isso produzirá o seguinte resultado -
Saída
Constructing an Employee Constructing an Employee Call mailCheck using Salary reference -- Within mailCheck of Salary class Mailing check to Mohd Mohtashim with salary 3600.0 Call mailCheck using Employee reference-- Within mailCheck of Salary class Mailing check to John Adams with salary 2400.0
Aqui, instanciamos dois objetos Salary. Um usando uma referência de salário s , e o outro usando uma referência de funcionário e .
Ao invocar s.mailCheck() , o compilador vê mailCheck() na classe Salary em tempo de compilação e a JVM chama mailCheck() na classe Salary em tempo de execução.
mailCheck() em e é bem diferente porque e é uma referência de funcionário. Quando o compilador vê e.mailCheck() , o compilador vê o método mailCheck() na classe Employee.
Aqui, em tempo de compilação, o compilador usou mailCheck() em Employee para validar essa instrução. No tempo de execução, entretanto, a JVM chama mailCheck() na classe Salary.
Esse comportamento é chamado de invocação de método virtual e esses métodos são chamados de métodos virtuais. Um método substituído é invocado em tempo de execução, independentemente do tipo de dados da referência que foi usada no código-fonte em tempo de compilação.
Java