O polimorfismo é um dos pilares fundamentais da Programação Orientada a Objetos (POO) e refere-se à capacidade de uma única interface ou método funcionar de diferentes maneiras para diferentes tipos de dados ou objetos. Em Java, o polimorfismo permite que um método ou uma classe tenha várias formas.
Existem dois tipos principais de polimorfismo em Java:
- Polimorfismo em Tempo de Compilação (Overloading)
- Polimorfismo em Tempo de Execução (Overriding)
Polimorfismo em Tempo de Compilação (Overloading)
O polimorfismo em tempo de compilação, ou sobrecarga de métodos, ocorre quando vários métodos em uma classe têm o mesmo nome, mas diferentes assinaturas (tipo e número de parâmetros).
Regras de Sobrecarga:
- Assinatura Diferente: Os métodos devem ter um número diferente de parâmetros ou tipos de parâmetros diferentes.
- Mesma Classe: A sobrecarga deve ocorrer dentro da mesma classe.
- Tipo de Retorno Não Importa: O tipo de retorno dos métodos sobrecarregados pode ser diferente, mas isso não é considerado na definição da sobrecarga.
- Modificadores de Acesso Podem Variar: Os métodos sobrecarregados podem ter diferentes modificadores de acesso (public, private, protected).
Exemplo de Sobrecarga de Métodos:
public class Calculadora {
// Método para somar dois inteiros
public int soma(int a, int b) {
return a + b;
}
// Método para somar três inteiros
public int soma(int a, int b, int c) {
return a + b + c;
}
// Método para somar dois números de ponto flutuante
public double soma(double a, double b) {
return a + b;
}
}
Quando vários métodos sobrecarregados estão presentes, o Java procura a correspondência mais próxima primeiro. Ele tenta encontrar o seguinte:
- Correspondência exata por tipo.
- Correspondência de um tipo de superclasse
- Convertendo para um tipo primitivo maior
- Convertendo para um tipo autoboxed
- Varargs
Polimorfismo em Tempo de Execução (Overriding)
O polimorfismo em tempo de execução, ou sobrescrita de métodos, ocorre quando uma classe derivada (subclasse) fornece uma implementação específica de um método que já é definido na sua classe base (superclasse). A sobrescrita permite que uma subclasse forneça uma implementação específica de um método que é já fornecido por uma de suas superclasses ou interfaces.
Regras de Sobrescrita:
- Mesma Assinatura: O método sobrescrito deve ter exatamente a mesma assinatura que o método na superclasse (mesmo nome, tipo de retorno e parâmetros).
- Tipo de Retorno Covariante: O tipo de retorno do método sobrescrito pode ser o mesmo ou um subtipo do tipo de retorno do método da superclasse.
- Modificadores de Acesso: O método sobrescrito não pode ter um modificador de acesso mais restritivo do que o método na superclasse. Por exemplo, se o método na superclasse é
public, o método sobrescrito não pode serprotectedouprivate. - Modificadores Especiais: Se o método na superclasse é marcado como
final,staticouprivate, ele não pode ser sobrescrito. - Uso da Anotação
@Override: Embora opcional, é uma boa prática usar a anotação@Overridepara indicar claramente que um método está sendo sobrescrito. - Exception: Se quaisquer exceções verificadas forem lançadas, somente as mesmas exceções ou subclasses dessas exceções poderão ser lançadas.
Exemplo de Sobrescrita de Métodos:
// Classe base
public class Animal {
public void fazerSom() {
System.out.println("O animal faz um som.");
}
}
// Classe derivada
public class Cachorro extends Animal {
@Override
public void fazerSom() {
System.out.println("O cachorro late.");
}
}
// Classe derivada
public class Gato extends Animal {
@Override
public void fazerSom() {
System.out.println("O gato mia.");
}
}
Uso do Polimorfismo em Tempo de Execução
Podemos ver o poder do polimorfismo em tempo de execução ao usar referências da superclasse para apontar para objetos de subclasses e chamar métodos sobrescritos:
public class TestePolimorfismo {
public static void main(String[] args) {
Animal meuAnimal = new Animal();
Animal meuCachorro = new Cachorro();
Animal meuGato = new Gato();
meuAnimal.fazerSom(); // Saída: O animal faz um som.
meuCachorro.fazerSom(); // Saída: O cachorro late.
meuGato.fazerSom(); // Saída: O gato mia.
}
}
Vantagens do Polimorfismo
- Flexibilidade e Reusabilidade: Permite que o mesmo código trabalhe com diferentes tipos de dados e classes, aumentando a reusabilidade do código.
- Manutenibilidade: Facilita a manutenção e a expansão do código, permitindo que novas classes e métodos sejam adicionados com menos impacto no código existente.
- Extensibilidade: Novas funcionalidades podem ser adicionadas com facilidade sem modificar o código existente.
Diferenças entre Sobrecarga e Sobrescrita
- Sobrecarga:
- Ocorre dentro da mesma classe.
- Os métodos têm o mesmo nome, mas diferentes assinaturas.
- Pode ter diferentes tipos de retorno.
- Pode ter diferentes modificadores de acesso.
- Sobrescrita:
- Ocorre entre uma superclasse e uma subclasse.
- Os métodos têm exatamente a mesma assinatura.
- O tipo de retorno deve ser o mesmo ou um subtipo (tipo de retorno covariante).
- O método sobrescrito deve ter o mesmo ou um menos restritivo modificador de acesso.
Resumo
O polimorfismo em Java é uma poderosa característica que permite a criação de código mais flexível, reutilizável e extensível. Ele pode ser implementado através de sobrecarga de métodos (polimorfismo em tempo de compilação) e sobrescrita de métodos (polimorfismo em tempo de execução). Isso permite que métodos com o mesmo nome atuem de maneiras diferentes dependendo dos objetos ou parâmetros com os quais são usados.