Sobrescritura de métodos (@Override)
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 22 de 87.
Sobrescritura de métodos permite que una subclase proporcione su propia implementación de un método que ya está definido en su clase padre. Así es como las clases hijas personalizan el comportamiento heredado para adaptarse a sus necesidades específicas.
Cuando sobrescribes un método, la versión de la subclase reemplaza completamente la versión del padre para los objetos de esa subclase. El método debe tener el mismo nombre, tipo de retorno y parámetros que el método del padre:
public class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
public class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}La anotación @Override le indica al compilador que tienes la intención de sobrescribir un método padre. Aunque es opcional, se recomienda encarecidamente porque el compilador detectará errores si escribes mal accidentalmente el nombre del método o utilizas parámetros incorrectos:
@Override
public void makeSound() { } // Correcto - el compilador verifica esto
public void makesound() { } // ¡Error tipográfico! Crea un nuevo método en lugar de sobrescribirCuando llamas a un método sobrescrito, Java ejecuta la versión que pertenece al tipo de objeto real:
Dog dog = new Dog();
dog.makeSound(); // Salida: Woof!
Cat cat = new Cat();
cat.makeSound(); // Salida: Meow!Recuerda de la lección anterior que puedes usar super.methodName() dentro de un método sobrescrito si aún deseas incluir el comportamiento del padre junto con tu nuevo código.
Desafío
FácilVamos a construir un sistema de notificaciones que demuestre cómo las subclases pueden sobrescribir los métodos de los padres para proporcionar su propio comportamiento especializado. Verás cómo la anotación @Override ayuda a detectar errores y cómo cada subclase puede personalizar los métodos heredados.
Crearás tres archivos para organizar tu código:
Notification.java: Crea la clase padre que representa una notificación genérica. Debe tener:- Un campo privado para el
message(String) - Un constructor que acepte el mensaje
- Un método
getMessage()que devuelva el mensaje - Un método
send()que imprima:Sending notification: [message]
- Un campo privado para el
EmailNotification.java: Crea una subclase que extienda de Notification y personalice cómo se envían las notificaciones por correo electrónico:- Un campo privado para la dirección de correo del
recipient(String) - Un constructor que reciba tanto el mensaje como el destinatario, utilizando
super(message)para la parte del padre - Sobrescribe el método
send()utilizando la anotación@Overridepara imprimir:Emailing [recipient]: [message]
- Un campo privado para la dirección de correo del
Main.java: Demuestra cómo el mismo nombre de método produce comportamientos diferentes según el tipo de objeto. Recibirás dos entradas: un mensaje y una dirección de correo electrónico. Crea tanto una Notification regular como una EmailNotification con el mismo mensaje, luego llama asend()en cada una para ver las diferentes salidas.
Recibirás dos entradas: el mensaje (String) y la dirección de correo electrónico del destinatario (String).
Tu salida debería mostrar dos líneas: primero del método send() del padre, luego de la versión sobrescrita en EmailNotification. Esto demuestra cómo la sobrescritura de métodos permite a las subclases reemplazar el comportamiento heredado con su propia implementación.
Hoja de referencia
La sobrescritura de métodos permite que una subclase proporcione su propia implementación de un método que ya ha sido definido en su clase padre. El método sobrescrito debe tener el mismo nombre, tipo de retorno y parámetros que el método del padre.
public class Animal {
public void makeSound() {
System.out.println("Some generic sound");
}
}
public class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}La anotación @Override es opcional pero muy recomendada. Le indica al compilador que tienes la intención de sobrescribir un método del padre, lo que ayuda a detectar errores como nombres de métodos mal escritos o parámetros incorrectos:
@Override
public void makeSound() { } // Correcto - el compilador verifica esto
public void makesound() { } // ¡Error tipográfico! Crea un nuevo método en lugar de sobrescribirAl llamar a un método sobrescrito, Java ejecuta la versión que pertenece al tipo de objeto real:
Dog dog = new Dog();
dog.makeSound(); // Output: Woof!
Cat cat = new Cat();
cat.makeSound(); // Output: Meow!Puedes usar super.methodName() dentro de un método sobrescrito para incluir el comportamiento del padre junto con tu nuevo código.
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 message = scanner.nextLine();
String recipient = scanner.nextLine();
// TODO: Crear un objeto Notification con el mensaje
// TODO: Crear un objeto EmailNotification con el mensaje y el destinatario
// TODO: Llamar a send() en el objeto Notification
// TODO: Llamar a send() en el objeto EmailNotification
}
}
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
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator