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
classAluno {
String nome; int idade; // repete
}
classProfessor {
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.
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 classPessoa {
protectedString nome;
protected int idade;
}
Aluno.java
public classAlunoextendsPessoa {
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
publicAluno(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
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
@OverridepublicStringapresentar() {
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 = newAluno(...); // uma Pessoa que é AlunoPessoa p2 = newProfessor(...); // 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 = newAluno(...);
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 = newArrayList<>();
comunidade.add(newAluno(...));
comunidade.add(newProfessor(...));
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 classPessoa {
String nome;
public abstractStringapresentar(); // 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 interfaceAvaliavel {
doublecalcularMedia(); // só a assinatura
}
classAlunoimplementsAvaliavel {
public doublecalcularMedia() { ... }
}
🤝
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 abstrata
Interface
Tem código pronto?
sim (alguns métodos)
essencialmente é só contrato
Tem atributos de estado?
sim
nã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 sobrescrevendoapresentar().
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 sobrescreverenderJuros() 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.
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".