Menu
Coddy logo textTech

Sobrescritura de métodos (tiempo de ejecución)

Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 29 de 87.

Mientras que la sobrecarga de métodos se resuelve en tiempo de compilación, la sobreescritura de métodos demuestra el polimorfismo en tiempo de ejecución. Aquí, Java decide qué método ejecutar cuando el programa se está ejecutando realmente, basándose en el tipo real del objeto en lugar del tipo declarado de la variable.

Aprendiste sobre la sobrescritura de métodos en el capítulo de Herencia. Lo que lo hace polimórfico es cómo Java lo maneja cuando una referencia de padre apunta a un objeto hijo:

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!");
    }
}

Ahora observa lo que sucede cuando usamos referencias de tipo padre:

Animal myPet = new Dog();
myPet.speak();  // Salida: Bark!

myPet = new Cat();
myPet.speak();  // Salida: Meow!

Aunque myPet está declarada como Animal, Java llama al método sobrescrito basándose en el tipo de objeto real en tiempo de ejecución. Esto se llama despacho dinámico de métodos. La JVM observa lo que el objeto es realmente, no lo que dice la variable que es.

Esto permite patrones potentes como el procesamiento de diferentes objetos de manera uniforme:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // Cada uno llama a su propia versión
}

El polimorfismo en tiempo de ejecución te permite escribir código flexible que funciona con tipos padre mientras utiliza automáticamente el comportamiento correcto de la clase hija.

challenge icon

Desafío

Fácil

Construyamos un sistema de notificaciones que demuestre el polimorfismo en tiempo de ejecución en acción. Crearás una jerarquía de tipos de notificación donde la misma llamada a un método produce diferentes salidas dependiendo del tipo de objeto real en tiempo de ejecución.

Organizarás tu código en cuatro archivos:

  • Notification.java: Crea la clase base que comparten todas las notificaciones. Cada notificación tiene un campo recipient (String). Incluye un constructor para inicializarlo, un método getter y un método send() que imprima: Sending notification to [recipient]
  • EmailNotification.java: Crea una clase que extienda de Notification. Las notificaciones por correo electrónico añaden un campo subject. Usa super para la inicialización del padre. Sobrescribe el método send() para que imprima: Emailing [recipient]: [subject]
  • SMSNotification.java: Crea otra clase que extienda de Notification. Las notificaciones por SMS añaden un campo phoneNumber. Sobrescribe send() para que imprima: Texting [phoneNumber] for [recipient]
  • Main.java: ¡Aquí es donde brilla el polimorfismo en tiempo de ejecución! Recibirás tres entradas: un nombre de destinatario, un asunto de correo electrónico y un número de teléfono. Crea un arreglo de referencias de Notification que contenga tres objetos en este orden: una Notification base, una EmailNotification y una SMSNotification (todas usando el mismo destinatario). Luego, recorre el arreglo y llama a send() en cada elemento. ¡Observa cómo Java llama automáticamente a la versión sobrescrita correcta basándose en lo que es realmente cada objeto!

Recibirás tres entradas: el nombre del destinatario (String), el asunto del correo electrónico (String) y el número de teléfono (String).

Tu salida debería mostrar tres líneas; cada llamada a send() produce una salida diferente a pesar de que estás llamando al mismo método en referencias de Notification. ¡Eso es el despacho dinámico de métodos en funcionamiento!

Hoja de referencia

La sobrescritura de métodos demuestra el polimorfismo en tiempo de ejecución, donde Java decide qué método ejecutar cuando el programa se está ejecutando, basándose en el tipo real del objeto en lugar del tipo declarado de la variable.

Cuando una referencia de padre apunta a un objeto hijo, Java llama al método sobrescrito basándose en el tipo de objeto real en tiempo de ejecución:

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();  // Salida: Bark!

myPet = new Cat();
myPet.speak();  // Salida: Meow!

Esto se llama despacho dinámico de métodos. La JVM observa lo que el objeto es realmente, no lo que dice la variable que es.

El polimorfismo en tiempo de ejecución permite procesar diferentes objetos de manera uniforme:

Animal[] pets = {new Dog(), new Cat(), new Dog()};
for (Animal pet : pets) {
    pet.speak();  // Cada uno llama a su propia versión
}

Pruébalo tú mismo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Leer entradas
        String recipient = scanner.nextLine();
        String subject = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        
        // TODO: Crear un arreglo de referencias de Notification con 3 elementos
        // El arreglo debe contener (en este orden):
        // 1. Un objeto Notification base
        // 2. Un objeto EmailNotification
        // 3. Un objeto SMSNotification
        // Todos usando el mismo recipient
        
        // TODO: Recorrer el arreglo y llamar a send() en cada elemento
        // ¡Esto demuestra el polimorfismo en tiempo de ejecución!
    }
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos