← → navega · N notas · F tela cheia
Módulo 4 de 5

Herança e
Polimorfismo

Reaproveitar e especializar moldes com herança e super, sobrescrever comportamento e deixar um mesmo código tratar muitas formas — com classes abstratas e interfaces.

Programação Orientada a Objetos em Java  ·  Docente: Profa. Andréa Barboza Proto Sardi
Abertura. Os dois pilares "avançados" da POO. Herança = reúso e organização; polimorfismo = flexibilidade. Tudo aplicado à família Pessoa → Aluno/Professor. É o módulo que mais "abre a cabeça".
Módulo 4 · Abertura

Objetivos de aprendizagem

🌳Herdar

Reaproveitar atributos e métodos com extends e super.

✍️Sobrescrever

Especializar comportamento herdado com @Override.

🎭Polimorfismo

Tratar objetos diferentes pela mesma referência, decidindo em tempo de execução.

📐Abstrair

Definir contratos com classes abstratas e interfaces.

Objetivos. Quatro competências encadeadas: herdar → sobrescrever → polimorfismo → abstrair. Polimorfismo só faz sentido depois de herança + sobrescrita; abstrações são a forma "limpa" de habilitá-lo.
Retomada · Situação-problema

Código repetido entre classes

repetição
class Aluno {
  String nome; int idade; // repete
}
class Professor {
  String nome; int idade; // repete!
}
💥
o problema
Aluno e Professor compartilham nome e idade. Copiar atributos em cada classe é desperdício e fonte de bugs.

A solução: criar uma classe Pessoa que concentra o comum, e fazer as outras herdarem dela.

Gancho. Mostre a duplicação literal. A herança "oferece solução tanto para o reúso quanto para a organização de programas" (fonte). Pessoa será a superclasse; Aluno e Professor, especializações.
Conceito 1

Herança: reusar e especializar

Uma classe-filha herda tudo da classe-mãe e pode adicionar ou modificar o que precisar.

Pessoanome · idade Aluno+ matricula Professor+ salario
Aluno e Professor herdam de Pessoa e acrescentam o que é específico.
🌳
relação "é um"
Aluno é uma Pessoa. Professor é uma Pessoa. Esse teste indica herança.
Herança. Teste "é um (is-a)" × "tem um (has-a, associação do M3)". Aluno é uma Pessoa → herança. Carro tem um Motor → composição. A seta UML aponta da filha para a mãe (triângulo vazio).
Conceito 2 · sintaxe

A palavra-chave extends

Pessoa.java
public class Pessoa {
    protected String nome;
    protected int idade;
}
Aluno.java
public class Aluno extends Pessoa {
    int matricula;  // só do Aluno
}
🧬
o que Aluno ganha
Aluno já tem nome e idade — sem reescrever. protected permite que a filha acesse esses atributos.

Java tem herança simples: uma classe estende apenas uma superclasse.

extends. Conecte protected (Módulo 2): visível para subclasses. Aluno reusa nome/idade automaticamente. Java não permite herança múltipla de classes (evita ambiguidade) — interfaces resolverão isso depois.
Conceito 3

A palavra-chave super

super chama o construtor ou um método da superclasse.

Aluno.java
public Aluno(String nome, int idade, int matricula) {
    super(nome, idade);   // inicializa a parte "Pessoa"
    this.matricula = matricula;
}
🔗
divisão de trabalho
super(...) cuida do que é da Pessoa; this cuida do que é só do Aluno.
super. Compare com this (Módulo 1): this = este objeto; super = a parte herdada. super(...) deve ser a primeira linha do construtor. Também serve para chamar um método da mãe que foi sobrescrito (super.metodo()). Divisão clara de responsabilidades.
Conceito 4

A raiz de tudo: Object

Object Pessoa Aluno
Toda classe descende, direta ou indiretamente, de Object.
💡
agora faz sentido
Por isso equals, hashCode e toString existem em qualquer objeto — vêm de Object.

Quando você "sobrescreve toString", está modificando um método que já foi herdado de Object.

Object. Fecha o ciclo do Módulo 2: o "trio" (equals/hashCode/toString) vinha de Object — agora a turma vê a hierarquia. Mesmo sem escrever extends, toda classe já estende Object implicitamente. É a raiz universal.
Conceito 5

Sobrescrita (override)

A filha redefine um método herdado, mantendo a mesma assinatura.

Aluno.java
@Override
public String apresentar() {
    return "Sou o aluno " + nome;   // versão do Aluno
}
✍️
@Override
A anotação avisa o compilador: "estou substituindo um método da superclasse". Se errar a assinatura, ele acusa.
Sobrescrita. Mesma assinatura da mãe, corpo novo. @Override é rede de segurança (pega erros de digitação na assinatura). Já usamos em equals/hashCode/toString no Módulo 2 — agora com a teoria completa. Prepara o polimorfismo.
Não confunda

Sobrecarga × sobrescrita

🔀Sobrecarga (overload)

Mesma classe, mesmo nome, assinaturas diferentes. Decidida em compilação.

✍️Sobrescrita (override)

Subclasse, mesma assinatura, corpo novo. Decidida em execução.

🧭
memorize
Sobrecarga = mesmo nome, formas diferentes (mesma classe). Sobrescrita = mesma forma, classe filha.
Overload × override. Confusão clássica. Tabela mental: carga = compilação/mesma classe/assinaturas distintas; escrita = execução/herança/mesma assinatura. O "decidida em execução" da sobrescrita é exatamente o que habilita polimorfismo.
Conceito 6

Polimorfismo: muitas formas

Uma referência do tipo mãe pode apontar para objetos de qualquer filha.

Main.java
Pessoa p1 = new Aluno(...);     // uma Pessoa que é Aluno
Pessoa p2 = new Professor(...); // uma Pessoa que é Professor
🎭
a flexibilidade
O tipo da variável é Pessoa; o tipo do objeto é Aluno ou Professor. Uma só "forma" de tratar muitos.
Polimorfismo. "Poli + morfos" = muitas formas. A variável-mãe aceita qualquer objeto-filha. Ainda não chamamos métodos — o impacto vem no próximo slide (qual método roda?). Conecte com referências do Módulo 1.
Conceito 7

Quem decide é o objeto, em execução

Main.java
Pessoa p = new Aluno(...);
System.out.println(p.apresentar());
// → "Sou o aluno ..."  (versão do Aluno!)
despacho dinâmico
Mesmo a variável sendo Pessoa, roda o apresentar() do objeto real (Aluno). A JVM decide em tempo de execução.
?
Verificação
Se p = new Professor(...), qual apresentar() roda?
Runtime. Resposta: o do Professor. Esse é o coração do polimorfismo: o método executado depende do objeto, não da variável. Liga direto com a sobrescrita. É também por isso que println(objeto) chama o toString certo (Módulo 2).
Aplicação

Uma lista, vários tipos

Main.java
ArrayList<Pessoa> comunidade = new ArrayList<>();
comunidade.add(new Aluno(...));
comunidade.add(new Professor(...));

for (Pessoa pessoa : comunidade) {
    System.out.println(pessoa.apresentar()); // cada um do seu jeito
}
💪
o ganho
Um único laço trata Alunos e Professores. Para adicionar um novo tipo (Funcionário), nada muda aqui.
Aplicação. Junta M3 (coleção) + M4 (polimorfismo): lista de Pessoa guarda subtipos misturados, e o for-each chama a versão certa de cada um. Esse é o argumento prático: código extensível e aberto a novos tipos sem reescrita.
Conceito 8

Classes abstratas

Um molde incompleto: define o comum, mas não pode ser instanciado sozinho.

Pessoa.java
public abstract class Pessoa {
    String nome;
    public abstract String apresentar(); // sem corpo: obrigatório implementar
}
// new Pessoa();  ✗ ERRO: classe abstrata não instancia
📐
quando usar
Quando "Pessoa" só existe como conceito — o que existe de fato é Aluno ou Professor.
Abstrata. Faz sentido modelar Pessoa, mas ninguém é "só uma Pessoa" — é sempre algo concreto. A classe abstrata força isso: não dá new Pessoa(). O método abstrato é um contrato: toda filha DEVE implementar apresentar().
Conceito 9

Interfaces: contrato puro

Uma interface define o que uma classe deve saber fazer, sem dizer como.

Avaliavel.java
public interface Avaliavel {
    double calcularMedia();  // só a assinatura
}

class Aluno implements Avaliavel {
    public double calcularMedia() { ... }
}
🤝
implements
Quem "assina" a interface obriga-se a fornecer todos os métodos dela.

Uma classe estende uma superclasse, mas pode implementar várias interfaces.

Interface. É o "contrato" que vimos com List/ArrayList no Módulo 3 (List é interface!). Interface = 100% contrato; classe abstrata pode ter código pronto. Implements permite múltiplas interfaces, contornando a herança simples.
Comparação

Classe abstrata × interface

Classe abstrataInterface
Tem código pronto?sim (alguns métodos)essencialmente é só contrato
Tem atributos de estado?simnão (apenas constantes)
Quantas posso usar?só 1 (extends)várias (implements)
Relação"é um" forte"é capaz de"
🧭
regra prática
Use abstrata quando há código comum a compartilhar; interface quando quer só garantir capacidades.
Comparação. Abstrata = "é um" + reúso de código. Interface = "é capaz de" (Avaliavel, Comparable). Muitas vezes se usam juntas. Não precisa esgotar — a intuição da última linha resolve a maioria das decisões iniciais.
Erros frequentes

Onde se tropeça

🧬Herdar sem ser "é um"

Fazer Carro extends Motor. Carro tem motor (composição), não é um motor.

🔗Esquecer o super(...)

Construtor da filha sem inicializar a parte da mãe → erro de compilação.

✍️Assinatura diferente

Achar que sobrescreveu, mas mudou os parâmetros → virou sobrecarga. @Override evita.

📐new em classe abstrata

Tentar new Pessoa() numa classe abstrata → não compila.

Erros. O nº 1 é abusar de herança onde cabe composição (is-a vs has-a). @Override previne o erro de assinatura silencioso. Reforce super(...) como primeira linha. Esses pontos caem muito em avaliação.
Prática guiada

Família Pessoa

🎯Construir juntos

1. Classe Pessoa (nome, idade) com método apresentar().
2. Aluno extends Pessoa e Professor extends Pessoa, cada um sobrescrevendo apresentar().
3. Crie uma ArrayList<Pessoa> com os dois e, num for-each, chame apresentar().

🎭
observe
Cada objeto responde do seu jeito — polimorfismo em ação, num laço só.
Prática guiada. O exercício-síntese do módulo: herança + super + sobrescrita + polimorfismo + coleção. Peça previsão da saída antes de rodar. Mostre que adicionar um terceiro tipo (Funcionario) não exige mudar o laço.
Exercício autônomo

Sua vez: Conta e ContaPoupança

📋Requisitos

Conta (do Módulo 1) com saldo e método renderJuros().
ContaPoupanca extends Conta que sobrescreve renderJuros() com taxa própria.
• Use super no construtor.
• Guarde várias contas numa ArrayList<Conta> e chame renderJuros() em todas via for-each.

!
Entrega
Saldos atualizados, cada conta rendendo conforme seu tipo (polimorfismo).
Exercício autônomo. Tempo: 25 min. Aplica herança a uma entidade já conhecida (Conta). O ponto-chave é a ContaPoupanca sobrescrever renderJuros e o polimorfismo escolher a versão certa na lista. Recolha as soluções.
Revisão · verificação

Revisão relâmpago

extends herda; super chama a mãe.
Sobrescrita redefine método herdado (mesma assinatura).
Polimorfismo: o objeto real decide o método, em execução.
Abstrata = molde incompleto · interface = contrato.
?
Questão 1
Quando usar herança e quando usar associação?
?
Questão 2
Em Pessoa p = new Aluno(), qual método roda em p.apresentar()?
?
Questão 3
Diferença entre classe abstrata e interface?
Revisão. Respostas: (1) herança = "é um"; associação = "tem um". (2) o apresentar() do Aluno (objeto real). (3) abstrata pode ter código/estado e é única (extends); interface é contrato e múltipla (implements). Ticket de saída.
Síntese do Módulo 4

O que levamos deste módulo

🌳Herança

Reúso e organização com extends e super.

🎭Polimorfismo

Uma referência, muitos comportamentos, decididos em execução.

📐Abstração

Classes abstratas e interfaces definem contratos.

➡️
próximo módulo
Robustez e Qualidade — tratar erros (exceções), organizar em pacotes e garantir tudo com princípios SOLID e testes JUnit.
Síntese. Os quatro pilares da POO estão completos (abstração, encapsulamento, herança, polimorfismo). Ponte para o M5: agora que o sistema é grande, precisamos torná-lo robusto e testável. Encerramento: "vocês modelam como profissionais; falta blindar e validar".
módulos
Nota de apresentação para a docente