Sobrescrita de Métodos (Tempo de Execução)
Parte da seção Programação Orientada a Objetos do Journey de Java da Coddy — lição 29 de 87.
Enquanto a sobrecarga de método é resolvida em tempo de compilação, a sobrescrita de método demonstra polimorfismo de tempo de execução. Aqui, o Java decide qual método executar quando o programa está realmente em execução, com base no tipo real do objeto, em vez do tipo declarado da variável.
Você aprendeu sobre a sobrescrita de métodos no capítulo sobre Herança. O que a torna polimórfica é como o Java a gerencia quando uma referência de pai aponta para um objeto de filho:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
public class Cat extends Animal {
@Override
public void speak() {
System.out.println("Meow!");
}
}Agora observe o que acontece quando usamos referências do tipo pai:
Animal myPet = new Dog();
myPet.speak(); // Saída: Bark!
myPet = new Cat();
myPet.speak(); // Saída: Meow!Embora myPet seja declarado como Animal, o Java chama o método sobrescrito com base no tipo real do objeto em tempo de execução. Isso é chamado de despacho dinâmico de métodos. A JVM olha para o que o objeto realmente é, não para o que a variável diz que ele é.
Isso permite padrões poderosos, como o processamento uniforme de diferentes objetos:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // Cada um chama sua própria versão
}O polimorfismo em tempo de execução permite que você escreva código flexível que funciona com tipos pai, enquanto usa automaticamente o comportamento correto do filho.
Desafio
FácilVamos construir um sistema de notificação que demonstra o polimorfismo em tempo de execução em ação. Você criará uma hierarquia de tipos de notificação onde a mesma chamada de método produz saídas diferentes dependendo do tipo real do objeto em tempo de execução.
Você organizará seu código em quatro arquivos:
Notification.java: Crie a classe base que todas as notificações compartilham. Cada notificação possui um camporecipient(String). Inclua um construtor para inicializá-lo, um método getter e um métodosend()que imprime:"Sending notification to " + recipientEmailNotification.java: Crie uma classe que estende Notification. As notificações por e-mail adicionam um camposubject. Usesuperpara a inicialização do pai. Sobrescreva o métodosend()para imprimir:"Emailing " + getRecipient() + ": " + subjectSMSNotification.java: Crie outra classe que estende Notification. As notificações por SMS adicionam um campophoneNumber. Sobrescrevasend()para imprimir:"Texting " + phoneNumber + " for " + getRecipient()Main.java: Aqui é onde o polimorfismo em tempo de execução brilha! Você receberá três entradas: um nome de destinatário, um assunto de e-mail e um número de telefone. Crie um array de referências deNotificationcontendo três objetos nesta ordem: uma Notification base, uma EmailNotification e uma SMSNotification (todas usando o mesmo destinatário). Em seguida, percorra o array e chamesend()em cada elemento. Observe como o Java chama automaticamente a versão sobrescrita correta com base no que cada objeto realmente é!
Você receberá três entradas: o nome do destinatário (String), o assunto do e-mail (String) e o número do telefone (String).
Sua saída deve mostrar três linhas - cada chamada de send() produz uma saída diferente, embora você esteja chamando o mesmo método em referências de Notification. Isso é o despacho dinâmico de métodos em funcionamento!
Folha de consulta
A sobrescrita de método demonstra o polimorfismo de tempo de execução, onde o Java decide qual método executar quando o programa está rodando, com base no tipo real do objeto em vez do tipo declarado da variável.
Quando uma referência de pai aponta para um objeto filho, o Java chama o método sobrescrito com base no tipo real do objeto em tempo de execução:
public class Animal {
public void speak() {
System.out.println("Some sound");
}
}
public class Dog extends Animal {
@Override
public void speak() {
System.out.println("Bark!");
}
}
Animal myPet = new Dog();
myPet.speak(); // Output: Bark!
myPet = new Cat();
myPet.speak(); // Output: Meow!Isso é chamado de despacho dinâmico de método. A JVM olha para o que o objeto realmente é, não para o que a variável diz que ele é.
O polimorfismo de tempo de execução permite processar diferentes objetos de forma uniforme:
Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
pet.speak(); // Cada um chama sua própria versão
}Experimente você mesmo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// Ler as entradas
String recipient = scanner.nextLine();
String subject = scanner.nextLine();
String phoneNumber = scanner.nextLine();
// TODO: Criar um array de referências Notification com 3 elementos
// O array deve conter (nesta ordem):
// 1. Um objeto Notification base
// 2. Um objeto EmailNotification
// 3. Um objeto SMSNotification
// Todos usando o mesmo recipient
// TODO: Percorrer o array e chamar send() em cada elemento
// Isso demonstra o polimorfismo em tempo de execução!
}
}
Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.
Todas as lições de Programação Orientada a Objetos
1Fundamentos de POO
Arquivos ExternosIntrodução à POOClasses vs ObjetosA Palavra-chave thisMétodosCampos (Atributos)Método ConstrutorSobrecarga de ConstrutorRecapitulação - Calculadora Simples4Herança
Herança Básica (extends)A Palavra-chave superSobrescrita de Método (@Override)Encadeamento de ConstrutoresA Classe ObjectHerança Simples e MultinívelPor que não há Herança Múltipla de ClassesRecapitulação - Hierarquia de Funcionários7Métodos Especiais e a Classe Object
Método toString()equals() e hashCode()Método clone()compareTo() e ComparableInterface ComparatorRecapitulação - Ordenação Personalizada2Modificadores de Acesso e Encapsulamento
Visão Geral dos Níveis de AcessoMétodos Getter e SetterOcultação de InformaçõesA Palavra-chave finalRecapitulação - Gerenciador de Conta Bancária5Polimorfismo
Fundamentos de Sobrecarga de MétodosSobrescrita de Métodos (Tempo de Execução)Upcasting e DowncastingO Operador instanceofClasses e Métodos AbstratosRecapitulação - Calculadora de Formas8Conceitos Avançados de OOP
Composição vs HerançaAgregação vs ComposiçãoClasses Internas, Aninhadas e AnônimasEnums e Métodos de EnumRecords (Java 16+)Classes Seladas (Java 17+)11Padrões de Projeto Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão BuilderPadrão ObserverPadrão Strategy3Propriedades de Classe e Membros Estáticos
Variáveis de Instância vs EstáticasMétodos EstáticosBlocos EstáticosConstantes (static final)Recapitulação - Contador e Utilitário6Interfaces e Classes Abstratas
Introdução às InterfacesImplementando InterfacesImplementação de Múltiplas InterfacesDefault e Static em InterfacesClasses Abstratas vs InterfacesInterfaces FuncionaisRecapitulação - Sistema de Pagamento9Generics
Introdução a GenericsClasses GenéricasMétodos GenéricosParâmetros de Tipos DelimitadosWildcards (?, extends, super)Recapitulação - Container Genérico12Padrões de Projeto Parte 2
Padrão CommandPadrão AdapterPadrão DecoratorPadrão Template MethodPadrão StatePadrão CompositePadrão Iterator