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