Polimorfismo, Binding e Generics

Aula 7 — Programação Orientada a Objetos

Autor

Marcos M. Raimundo — Instituto de Computação, UNICAMP

Data de Publicação

26 de agosto de 2026

Slides Lista de aulas

1 Proposta da Aula

A Aula 6 nos deu o contrato (Pagavel) e o vocabulário de defesa (exceções). Falta explicar como o compilador aceita que a mesma variável Pagavel forma receba, em momentos diferentes, um Pix ou um Cartao — duas classes sem nada em comum em dados. A resposta é o Polimorfismo.

O roteiro, em quatro perguntas:

  1. Quem decide qual código realmente roda quando chamamos forma.criarCobranca() — o compilador ou a máquina virtual?
  2. Existe só um tipo de polimorfismo, ou a sobrecarga de métodos conta como um também?
  3. Como o polimorfismo elimina, de vez, as longas cadeias de if/else baseadas em tipo?
  4. Por que uma lista “aceita-tudo” é um risco, e como Generics resolve isso sem perder flexibilidade?

2 Polimorfismo e o Mecanismo de Late Binding

Polimorfismo (do grego, “muitas formas”): a capacidade de uma variável ou método assumir diferentes comportamentos dependendo do objeto real que manipula. Se a interface deu o contrato, o polimorfismo é o mecanismo que permite que diferentes classes cumpram esse contrato de maneiras distintas, sem que o sistema precise ser reescrito para cada uma.

O mecanismo central é o Late Binding (ou Dynamic Dispatch). Ao declarar Pagavel forma, o compilador faz uma checagem estática: só garante que criarCobranca() existe na interface Pagavel. Ele não sabe — e não precisa saber — qual código roda de fato. Essa decisão só acontece em tempo de execução: a JVM olha o objeto real na Heap e desvia o fluxo para a implementação certa.

List<Pagavel> opcoes = clienteLogado.consultaMeiosPagamento();
Pagavel forma = opcoes.get(escolha - 1); // Pix, Boleto ou Cartao — tanto faz
String id = forma.criarCobranca(total);  // o comportamento real emerge em RUNTIME

O que praticamos aqui é o Polimorfismo de Inclusão: um subtipo (Pix) pode ser usado em qualquer lugar em que seu supertipo (Pagavel) é esperado. Se adicionarmos Cripto implements Pagavel amanhã, nenhuma linha do controlador muda — o sistema fica “Aberto para Extensão, Fechado para Modificação” (OCP).

3 A Taxonomia de Cardelli-Wegner

Polimorfismo não é um conceito único. Cardelli e Wegner organizam-no em dois eixos:

Universal — funciona para um número infinito de tipos:

  • Inclusão (Subtipagem): o pilar da OO — qualquer classe que implemente Pagavel serve.
  • Paramétrico (Generics): List<T> funciona da mesma forma para Produto ou Cliente, sem exigir hierarquia de herança.

Ad-hoc — funciona só para um conjunto finito e específico de tipos, resolvido cedo pelo compilador:

  • Sobrecarga (Overloading):
public class Notificador {
    public void enviar(String email) { ... }                  // versao 1
    public void enviar(String email, String assunto) { ... }   // versao 2 (sobrecarga)
}

Crítica arquitetural: a sobrecarga é um “polimorfismo aparente”. Se surgir um novo tipo de contato, é preciso voltar a Notificador e escrever uma nova versão — não resolve extensibilidade, viola o OCP.

  • Coerção: conversão implícita feita pela linguagem —
double preco = 100;       // int vira double
double total = preco + 5; // literal 5 vira double
String msg = "Total: " + total; // double vira String

É um comportamento embutido e rígido, sem troca de comportamento baseada em lógica de negócio.

A tabela que resume tudo:

Característica Static (Early) Binding Dynamic (Late) Binding
Quando ocorre Tempo de compilação Tempo de execução
Quem decide O compilador (tipo da variável) A JVM (objeto real na memória)
Exemplos Sobrecarga, Coerção, métodos static/private Polimorfismo de Inclusão (interfaces, herança)
Vantagem Performance levemente superior Desacoplamento total e extensibilidade

A OO aposta no Dynamic Binding porque é ele que permite trocar o “motor” de uma aplicação inteira — alterando uma linha de configuração — sem que o código cliente jamais saiba da mudança.

DicaPor que a sobrecarga de métodos é chamada de “polimorfismo aparente”, mesmo permitindo múltiplas versões do mesmo nome de método?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

4 O Fim do “Mar de IFs” e o Princípio Aberto/Fechado

Sem polimorfismo, cada novo tipo de pagamento exige mais um else if:

// Codigo SEM polimorfismo: rigido e fragil
public void processarPagamento(String tipo, double valor) {
    if (tipo.equals("PIX")) {
        pixService.gerarQR(valor);
    } else if (tipo.equals("BOLETO")) {
        boletoService.gerarLinhaDigitavel(valor);
    } else if (tipo.equals("CARTAO")) {
        cartaoService.autorizar(valor);
    }
    // Toda vez que um novo metodo surge, voce precisa "abrir" este codigo!
}

Com polimorfismo, o fluxo vira uma linha:

// Nao importa o que 'formaEscolhida' e, desde que seja Pagavel
String id = formaEscolhida.criarCobranca(total);

Se CriptoPayment implements Pagavel surgir amanhã, esse código não muda. É exatamente o Princípio Aberto/Fechado (OCP): entidades de software devem estar abertas para extensão (novas classes) e fechadas para modificação (o código que já funciona continua intocado).

Conceito Aberto para Extensão Fechado para Modificação
Aplicação Nova classe implementa Pagavel Checkout não precisa ser alterado
Mecanismo Novas classes concretas Programação para interfaces (Dynamic Binding)

A meta-regra prática: se você precisa de um if (ou switch, ou instanceof) para verificar o tipo de um objeto de negócio, você provavelmente está violando o OCP e perdendo a chance de deixar o polimorfismo fazer esse trabalho.

DicaSe amanhã surgir um novo meio de pagamento, o que exatamente muda no CheckoutController — e o que isso diz sobre o OCP?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

5 Generics: da Lista “Aceita-Tudo” à Etiqueta de Tipo

Antes do Java 5, uma coleção era um saco de Object — qualquer coisa podia entrar:

// Sem Generics: nada impede um erro de lógica
opcoesReais.add("Nao sou um pagamento"); // compila! Bomba-relogio.

O problema é a detecção tardia: o erro de lógica acontece na inserção, mas o programa só “explode” muito depois, quando o código tenta usar o objeto como se fosse Pagavel — um ClassCastException em tempo de execução, longe da causa real.

Generics resolvem isso com uma “etiqueta de tipo”:

// O compilador garante que esta lista SO tera Pagaveis
List<Pagavel> opcoesReais = clienteLogado.consultaMeiosPagamento();
// opcoesReais.add("Nao sou um pagamento"); // ERRO DE COMPILACAO, agora!

Quatro vantagens práticas:

  1. Robustez: move a detecção de erro do tempo de execução para o tempo de compilação.
  2. Legibilidade: List<Pagavel> já diz muito mais do que List sozinho.
  3. Reutilização: algoritmos genéricos continuam seguros para qualquer tipo (uma lista que ordena qualquer Comparable, por exemplo).
  4. Performance: evita casts repetidos e desnecessários no código.

Generics são, na prática, o Polimorfismo Paramétrico da taxonomia de Cardelli-Wegner em ação: o mesmo algoritmo de lista funciona para Pagavel, Produto ou Cliente, sem que nenhuma dessas classes precise pertencer à mesma hierarquia de herança.

DicaGenerics é uma instância de qual tipo de polimorfismo na taxonomia de Cardelli-Wegner — e por que ele não exige que os tipos usados pertençam à mesma hierarquia de herança?

Escreva sua resposta e compare com um colega antes de avançar (2 min).

6 Conclusão

Voltando às quatro perguntas da abertura:

  1. Quem decide o código real: a JVM, em tempo de execução, olhando o objeto na Heap — não o compilador.
  2. Um só tipo de polimorfismo? Não — Universal (Inclusão, Paramétrico) e Ad-hoc (Sobrecarga, Coerção), com garantias bem diferentes de extensibilidade.
  3. O fim do “Mar de IFs”: delegar a decisão para o próprio objeto, em vez de perguntar “qual é o seu tipo?” a cada chamada.
  4. A lista “aceita-tudo”: Generics move o erro do tempo de execução para o tempo de compilação, sem custo de flexibilidade.

Ponte para a Aula 8

Interfaces e Polimorfismo de Inclusão resolveram contratos entre classes sem parentesco algum. Existe outro mecanismo de polimorfismo — mais antigo, mais poderoso e mais perigoso — que compartilha estrutura, não só comportamento: a Herança. A Aula 8 explora esse mecanismo e o Princípio da Substituição de Liskov, a regra que decide quando herdar é seguro.

7 Exercícios

7.1 Questões discursivas

  1. Defina Static (Early) Binding e Dynamic (Late) Binding, explicitando em qual momento do ciclo de vida do software cada um atua e qual agente (compilador ou JVM) toma a decisão. Por que o desacoplamento em Orientação a Objetos depende criticamente do Late Binding?

  2. Um desenvolvedor júnior escreve um método efetuarCobranca que inspeciona uma String tipoPagamento numa cadeia de if/else para disparar PixService ou BoletoService. Critique esse design com base no Princípio Aberto/Fechado e mostre, em texto, como o Polimorfismo de Inclusão elimina esse sintoma.

  3. Explique o cenário de “Heterogeneidade Acidental” numa lista sem Generics num sistema de checkout, e como List<Pagavel> protege o usuário final de um ClassCastException em produção.

7.2 Questões de Verdadeiro/Falso

Cada bloco de 4 itens trata do mesmo tema. A questão só é considerada correta se todos os 4 itens forem julgados corretamente (deixar em branco tem penalidade de 20% da nota da questão).

DicaO que é Polimorfismo
  1. ( ) Polimorfismo é a capacidade de uma variável ou método assumir comportamentos diferentes conforme o objeto real manipulado.
  2. ( ) O objetivo prático do polimorfismo é reduzir o acoplamento e aumentar a extensibilidade do sistema.
  3. ( ) pagamentoConfirmado() pode se comportar como consulta de API no Pix e como verificação de data no Boleto.
  4. ( ) O código que chama a operação precisa conhecer os detalhes de implementação de quem a executa.
DicaLate Binding (Dynamic Dispatch)
  1. ( ) O compilador, ao ver Pagavel forma, verifica apenas que o método chamado existe na interface.
  2. ( ) A decisão de qual implementação executar ocorre em tempo de execução, pela JVM.
  3. ( ) A JVM decide olhando o tipo declarado da variável, não o objeto real na memória.
  4. ( ) Late Binding é o que permite trocar a implementação injetada sem que o código cliente perceba.
DicaPolimorfismo Universal
  1. ( ) O Polimorfismo de Inclusão permite que qualquer implementação de uma interface seja usada no lugar do tipo mais genérico.
  2. ( ) O Polimorfismo Paramétrico exige que todos os tipos usados pertençam à mesma hierarquia de herança.
  3. ( ) List<T> funciona da mesma forma para Produto ou Cliente, sem exigir parentesco entre as classes.
  4. ( ) O Polimorfismo Universal é chamado assim porque funciona sobre um número, em princípio, infinito de tipos.
DicaPolimorfismo Ad-hoc
  1. ( ) A sobrecarga (overloading) permite vários métodos com o mesmo nome e assinaturas distintas na mesma classe.
  2. ( ) A sobrecarga é considerada um polimorfismo verdadeiramente dinâmico e extensível.
  3. ( ) A escolha de qual método sobrecarregado roda é feita pelo compilador, com base nos tipos dos argumentos.
  4. ( ) A coerção é a conversão implícita de tipos, como int para double, embutida na linguagem.
DicaBinding Estático vs. Dinâmico
  1. ( ) O Static Binding ocorre em tempo de compilação; o Dynamic Binding, em tempo de execução.
  2. ( ) Métodos static e private em Java são resolvidos via Dynamic Binding.
  3. ( ) O Dynamic Binding é o que sustenta o desacoplamento e a extensibilidade típicos da Orientação a Objetos.
  4. ( ) A Injeção de Dependência se apoia no Dynamic Binding para trocar implementações sem alterar o código cliente.
DicaO Fim do “Mar de IFs”
  1. ( ) Uma cadeia de if/else verificando o tipo de um objeto de negócio é um sintoma clássico de violação do OCP.
  2. ( ) Com polimorfismo, adicionar um novo meio de pagamento não exige alterar o código do controlador.
  3. ( ) O uso de instanceof para decidir o comportamento com base no tipo concreto é incentivado pelo polimorfismo.
  4. ( ) Delegar a decisão para o próprio objeto substitui a necessidade de perguntar “qual é o seu tipo?”.
DicaO Princípio Aberto/Fechado (OCP)
  1. ( ) Um sistema está “aberto para extensão” quando comportamentos novos entram via novas classes, não via edição das antigas.
  2. ( ) Estar “fechado para modificação” significa que o código cliente não precisa ser retestado ao aceitar um novo ator.
  3. ( ) Programar para interfaces, em vez de classes concretas, é o mecanismo que viabiliza o cumprimento do OCP.
  4. ( ) O OCP recomenda revisar e reescrever o código cliente a cada novo comportamento adicionado ao sistema.
DicaSubstitutibilidade
  1. ( ) A substitutibilidade é a capacidade de trocar implementações concretas por uma abstração comum sem impacto perceptível.
  2. ( ) CheckoutController trata Pix, Boleto e Cartao de forma uniforme através da “lente” do tipo Pagavel.
  3. ( ) Para usar criarCobranca(), o controlador precisa saber qual classe concreta está por trás da referência.
  4. ( ) A substitutibilidade é o que permite ao sistema aceitar novos meios de pagamento sem alterar o núcleo.
DicaO Problema dos Raw Types
  1. ( ) Sem Generics, uma coleção Java podia armazenar referências genéricas do tipo Object.
  2. ( ) O risco de inserir um objeto incompatível numa lista sem Generics é detectado imediatamente pelo compilador.
  3. ( ) Recuperar dados de uma coleção não parametrizada exigia casting manual, sujeito a ClassCastException.
  4. ( ) A detecção tardia de um erro de tipo é o principal risco prático dos raw types.
DicaGenerics como Etiqueta de Segurança
  1. ( ) List<Pagavel> desloca a detecção de erros de tipo do tempo de execução para o tempo de compilação.
  2. ( ) Generics elimina a necessidade de casting manual na leitura de itens de uma coleção parametrizada.
  3. ( ) Generics reduz a legibilidade do código, pois exige mais caracteres na declaração de tipos.
  4. ( ) Proibir a inserção de tipos incompatíveis é uma das formas como Generics aumenta a robustez do sistema.
DicaPolimorfismo Paramétrico e Identidade
  1. ( ) O Polimorfismo Paramétrico usa variáveis de tipo (como <T>) mantendo a segurança de tipos estáticos.
  2. ( ) Diferente do Polimorfismo de Inclusão, o Paramétrico não exige que os tipos usados pertençam à mesma árvore de herança.
  3. ( ) Um Repositorio<T> genérico centraliza a lógica de infraestrutura sem duplicar código para cada entidade.
  4. ( ) Recuperar um item de um Repositorio<T> obriga o desenvolvedor a fazer um cast manual para o tipo concreto.
DicaSíntese: Contrato, Polimorfismo e Segurança de Tipo
  1. ( ) Interfaces definem o contrato; Polimorfismo de Inclusão o torna substituível; Generics protege coleções desses contratos.
  2. ( ) O uso conjunto de interfaces, polimorfismo e Generics é o que sustenta sistemas verdadeiramente Plug-and-Play.
  3. ( ) Um sistema bem desenhado com essas três ferramentas ainda pode exigir ifs de tipo em pontos centrais do fluxo.
  4. ( ) A meta comum às três ferramentas é mover decisões e erros para o momento mais cedo e mais seguro possível do ciclo de vida do software.