Menu
Coddy logo textTech

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.

challenge icon

Desafio

Fácil

Vamos 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 campo recipient (String). Inclua um construtor para inicializá-lo, um método getter e um método send() que imprime: "Sending notification to " + recipient
  • EmailNotification.java: Crie uma classe que estende Notification. As notificações por e-mail adicionam um campo subject. Use super para a inicialização do pai. Sobrescreva o método send() para imprimir: "Emailing " + getRecipient() + ": " + subject
  • SMSNotification.java: Crie outra classe que estende Notification. As notificações por SMS adicionam um campo phoneNumber. Sobrescreva send() 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 de Notification contendo 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 chame send() 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!
    }
}
quiz iconTeste seus conhecimentos

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