Entendendo Polimorfismo em Java: Conceitos e Uso Prático
Polimorfismo é a capacidade de um objeto assumir muitas formas. O uso mais comum em OOP ocorre quando uma referência de classe pai refere-se a um objeto de classe filha.
Qualquer objeto Java que passe em mais de um teste IS-A é considerado polimórfico. Em Java, todos os objetos são polimórficos, pois qualquer objeto passa no teste IS-A para seu próprio tipo e para a classe Object.
Importante: a única forma de acessar um objeto é através de uma variável de referência. Uma variável de referência tem apenas um tipo. Uma vez declarada, seu tipo não pode ser alterado.
A variável pode ser reatribuída a outros objetos (se não for final). O tipo da referência determina quais métodos podem ser invocados no objeto.
Uma variável de referência pode referenciar qualquer objeto de seu tipo declarado ou qualquer subtipo. Pode ser declarada como tipo de classe ou interface.
Exemplo
public interface Vegetarian { }
public class Animal { }
public class Deer extends Animal implements Vegetarian { }
A classe Deer é polimórfica (múltipla herança via interface). Verdades:
- Deer IS-A Animal
- Deer IS-A Vegetarian
- Deer IS-A Deer
- Deer IS-A Object
Declarações legais para referência a objeto Deer:
Deer d = new Deer();
Animal a = d;
Vegetarian v = d;
Object o = d;
Todas as referências d, a, v, o apontam para o mesmo objeto Deer no heap.
Métodos Virtuais
O comportamento de métodos sobrescritos em Java permite aproveitar polimorfismo no design de classes.
Já discutimos sobrescrita: classe filha sobrescreve método do pai. Método sobrescrito fica "escondido" na classe pai e não é invocado a menos que a filha use super.
Exemplo: Employee e Salary
/* 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("Construindo um Employee");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("Enviando cheque para " + this.name + " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
// getters e setters...
public String getName() { return name; }
public String getAddress() { return address; }
public void setAddress(String newAddress) { address = newAddress; }
public int getNumber() { return number; }
}
/* File name: Salary.java */
public class Salary extends Employee {
private double salary;
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Dentro de mailCheck da classe Salary");
System.out.println("Enviando cheque para " + getName() + " com salário " + salary);
}
public double getSalary() { return salary; }
public void setSalary(double newSalary) {
if (newSalary >= 0.0) salary = newSalary;
}
public double computePay() {
System.out.println("Calculando salário para " + getName());
return salary / 52;
}
}
Programa de Demonstração
/* 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("Chamando mailCheck usando referência Salary --");
s.mailCheck();
System.out.println("\nChamando mailCheck usando referência Employee --");
e.mailCheck();
}
}
Saída:
Construindo um Employee
Construindo um Employee
Chamando mailCheck usando referência Salary --
Dentro de mailCheck da classe Salary
Enviando cheque para Mohd Mohtashim com salário 3600.0
Chamando mailCheck usando referência Employee --
Dentro de mailCheck da classe Salary
Enviando cheque para John Adams com salário 2400.0
Instanciamos dois objetos Salary. Um com referência Salary s, outro com referência Employee e.
- Ao invocar
s.mailCheck(), compilador vêmailCheck()emSalaryem tempo de compilação, e JVM invocamailCheck()deSalaryem tempo de execução. - Em
e.mailCheck(),eé referênciaEmployee. Compilador vêmailCheck()emEmployeepara validar. Em tempo de execução, JVM invocamailCheck()deSalary.
Esse comportamento é invocação de método virtual, e esses métodos são métodos virtuais. Um método sobrescrito é invocado em tempo de execução, independentemente do tipo de referência usado no código fonte em tempo de compilação.
Java
- Dominando Operadores Java: Tipos, Sintaxe e Exemplos Práticos
- Comentários em Java: Tipos, Uso e Boas Práticas
- Dominando o Loop for Aprimorado em Java: Sintaxe, Exemplos e Boas Práticas
- Dominando Strings em Java: Criação, Métodos e Boas Práticas
- Dominando Interfaces em Java: Conceitos, Implementação e Boas Práticas
- Dominando Classes Internas Anônimas em Java: Definição, Sintaxe e Exemplos Práticos
- Dominando Try-With-Resources em Java: Gerenciamento Automático de Recursos Explicado
- Anotações em Java Explicadas: Tipos, Posicionamento e Exemplos Práticos
- Asserções em Java: Guia Prático para Detectar Bugs e Garantir Confiabilidade do Código
- Entendendo o Java Vector: Uso, Recursos e Comparação com ArrayList