Polimorfismo
O polimorfismo é um dos quatro pilares fundamentais da Programação Orientada a Objetos (POO). O termo vem do grego "poly" (muitos) e "morphos" (formas), significando "muitas formas". Este conceito permite que objetos de diferentes classes sejam tratados como objetos de uma classe comum, possibilitando um código mais flexível e dinâmico.
Conceito Fundamental
O polimorfismo permite que um mesmo método ou operador tenha diferentes comportamentos dependendo do contexto em que é utilizado. Ele está intimamente ligado à herança, pois permite que métodos herdados sejam sobrescritos nas subclasses para se comportarem de maneira diferente.
Exemplo: O método emitirSom() pode fazer com que um Cachorro lata e um Gato mie, mesmo que ambos sejam chamados através de uma referência para Animal.
Tipos de Polimorfismo
1. Polimorfismo de Sobrecarga (Compile-time/Estático)
Ocorre quando múltiplos métodos com o mesmo nome coexistem na mesma classe, mas com diferentes parâmetros (número, tipo ou ordem). A decisão sobre qual método executar é tomada durante a compilação.
class Calculadora {
// Métodos sobrecarregados
public int somar(int a, int b) {
return a + b;
}
public double somar(double a, double b) {
return a + b;
}
public int somar(int a, int b, int c) {
return a + b + c;
}
}
2. Polimorfismo de Sobreposição/Substituição (Runtime/Dinâmico)
Ocorre quando uma subclasse fornece uma implementação específica para um método já definido na superclasse. A decisão sobre qual método executar é tomada durante a execução (runtime), baseada no tipo do objeto.
class Animal {
public void emitirSom() {
System.out.println("Som genérico de animal");
}
}
class Cachorro extends Animal {
@Override
public void emitirSom() {
System.out.println("Au au!");
}
}
class Gato extends Animal {
@Override
public void emitirSom() {
System.out.println("Miau!");
}
}
// Uso do polimorfismo
Animal animal1 = new Cachorro();
Animal animal2 = new Gato();
animal1.emitirSom(); // Saída: "Au au!"
animal2.emitirSom(); // Saída: "Miau!"
3. Polimorfismo Paramétrico (Generics)
Permite escrever código que pode trabalhar com diferentes tipos de dados. É implementado através de genéricos ou templates em linguagens como Java, C# e C++.
// Exemplo em Java com Generics
public class Caixa {
private T conteudo;
public void guardar(T item) {
conteudo = item;
}
public T obter() {
return conteudo;
}
}
// Uso
Caixa caixaDeTexto = new Caixa<>();
caixaDeTexto.guardar("Olá Mundo");
String texto = caixaDeTexto.obter();
Caixa caixaDeNumero = new Caixa<>();
caixaDeNumero.guardar(42);
Integer numero = caixaDeNumero.obter();
4. Polimorfismo de Coerção (Casting)
Ocorre quando um valor é convertido de um tipo de dados para outro. Isso pode ser feito explicitamente pelo programador ou implicitamente pelo compilador.
int numero = 10;
double decimal = numero; // Coerção implícita
double pi = 3.14159;
int piArredondado = (int) pi; // Coerção explícita
Benefícios do Polimorfismo
- Flexibilidade: Permite que o código trabalhe com objetos de diferentes tipos de maneira uniforme.
- Extensibilidade: Facilita a adição de novos tipos sem modificar o código existente.
- Reusabilidade: Promove o reuso de código através de interfaces e classes abstratas.
- Desacoplamento: Reduz a dependência entre diferentes partes do sistema.
Princípio da Substituição de Liskov
Este princípio, formulado por Barbara Liskov, é fundamental para o uso correto do polimorfismo:
"Se S é um subtipo de T, então objetos do tipo T podem ser substituídos por objetos do tipo S sem alterar nenhuma das propriedades desejáveis do programa."
Ou seja, uma subclasse deve ser capaz de substituir sua superclasse sem quebrar a funcionalidade do programa.
Polimorfismo com Interfaces
Interfaces são uma ferramenta poderosa para implementar polimorfismo, especialmente em linguagens que não suportam herança múltipla como Java.
interface Reproduzivel {
void reproduzir();
}
class MP3Player implements Reproduzivel {
@Override
public void reproduzir() {
System.out.println("Reproduzindo arquivo MP3");
}
}
class VideoPlayer implements Reproduzivel {
@Override
public void reproduzir() {
System.out.println("Reproduzindo arquivo de vídeo");
}
}
// Uso do polimorfismo com interfaces
Reproduzivel media1 = new MP3Player();
Reproduzivel media2 = new VideoPlayer();
media1.reproduzir(); // Saída: "Reproduzindo arquivo MP3"
media2.reproduzir(); // Saída: "Reproduzindo arquivo de vídeo"
Polimorfismo em Diferentes Linguagens
Polimorfismo em C++
// C++ usa funções virtuais para polimorfismo
class Animal {
public:
virtual void emitirSom() {
cout << "Som genérico de animal" << endl;
}
};
class Cachorro : public Animal {
public:
void emitirSom() override {
cout << "Au au!" << endl;
}
};
// Uso
Animal* animal = new Cachorro();
animal->emitirSom(); // Saída: "Au au!"
Polimorfismo em Python
# Python tem polimorfismo sem tipagem explícita
class Animal:
def emitir_som(self):
print("Som genérico de animal")
class Cachorro(Animal):
def emitir_som(self):
print("Au au!")
# Duck Typing - conceito próprio de Python
def fazer_barulho(animal):
animal.emitir_som()
animal = Animal()
cachorro = Cachorro()
fazer_barulho(animal) # Saída: "Som genérico de animal"
fazer_barulho(cachorro) # Saída: "Au au!"
Polimorfismo em JavaScript
// JavaScript usa protótipos e é dinamicamente tipado
class Animal {
emitirSom() {
console.log("Som genérico de animal");
}
}
class Cachorro extends Animal {
emitirSom() {
console.log("Au au!");
}
}
const animal = new Animal();
const cachorro = new Cachorro();
animal.emitirSom(); // Saída: "Som genérico de animal"
cachorro.emitirSom(); // Saída: "Au au!"
Casos de Uso Práticos
Sistema de Formas Geométricas
abstract class Forma {
abstract double calcularArea();
}
class Circulo extends Forma {
private double raio;
public Circulo(double raio) {
this.raio = raio;
}
@Override
double calcularArea() {
return Math.PI * raio * raio;
}
}
class Retangulo extends Forma {
private double largura;
private double altura;
public Retangulo(double largura, double altura) {
this.largura = largura;
this.altura = altura;
}
@Override
double calcularArea() {
return largura * altura;
}
}
// Uso
Forma forma1 = new Circulo(5);
Forma forma2 = new Retangulo(4, 6);
System.out.println("Área do círculo: " + forma1.calcularArea());
System.out.println("Área do retângulo: " + forma2.calcularArea());
Sistema de Pagamento
interface MetodoPagamento {
void processarPagamento(double valor);
}
class CartaoCredito implements MetodoPagamento {
private String numero;
public CartaoCredito(String numero) {
this.numero = numero;
}
@Override
public void processarPagamento(double valor) {
System.out.println("Processando pagamento de R$" + valor + " com cartão de crédito " + numero);
}
}
class PayPal implements MetodoPagamento {
private String email;
public PayPal(String email) {
this.email = email;
}
@Override
public void processarPagamento(double valor) {
System.out.println("Processando pagamento de R$" + valor + " via PayPal para " + email);
}
}
// Uso
class Loja {
public void checkout(double valorTotal, MetodoPagamento metodoPagamento) {
metodoPagamento.processarPagamento(valorTotal);
}
}
Conclusão
O polimorfismo é um dos conceitos mais poderosos da programação orientada a objetos, permitindo criar sistemas altamente flexíveis, extensíveis e fáceis de manter. Quando combinado com os outros pilares da POO (abstração, encapsulamento e herança), o polimorfismo proporciona uma base sólida para o desenvolvimento de software robusto e adaptável.
Na prática, o polimorfismo é amplamente utilizado em frameworks e bibliotecas, possibilitando extensões e personalizações sem modificar o código-fonte original. Ele é essencial em padrões de projeto como Strategy, Factory Method, Adapter, entre outros.