Métodos Default y Static en Interfaces
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy. Lección 37 de 87.
Originalmente, las interfaces solo podían contener métodos abstractos sin implementación. A partir de Java 8, las interfaces adquirieron la capacidad de incluir métodos predeterminados y métodos estáticos, lo que las hizo más flexibles.
Un método predeterminado proporciona una implementación directamente en la interfaz mediante la palabra clave default. Las clases que implementan la interfaz heredan este comportamiento automáticamente, pero pueden sobrescribirlo si es necesario:
public interface Drawable {
void draw();
default void display() {
System.out.println("Displaying on screen...");
draw();
}
}Cualquier clase que implemente Drawable obtiene el método display() gratis. Esto resulta especialmente útil para añadir nuevos métodos a interfaces existentes sin romper las clases que ya los implementan.
Los métodos estáticos en las interfaces funcionan como los métodos estáticos en las clases. Pertenecen a la propia interfaz y se llaman usando el nombre de la interfaz:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Uso
int result = MathOperations.add(5, 3);Los métodos estáticos de las interfaces no se pueden sobrescribir y las clases que las implementan no los heredan. Son útiles para métodos de utilidad relacionados con el propósito de la interfaz.
Si una clase implementa dos interfaces con la misma firma de método predeterminado, la clase debe sobrescribir ese método para resolver el conflicto.
Desafío
FácilConstruyamos un sistema de mensajería que muestre tanto los métodos predeterminados como los estáticos en las interfaces. Crearás una interfaz con funcionalidad incorporada que las clases que la implementen puedan usar directamente o personalizar según sea necesario.
Organizarás tu código en cuatro archivos:
Messenger.java: Define una interfaz que represente cualquier servicio de mensajería. Debe declarar un método abstractosendMessage(String message)que devuelva void. Añade un método predeterminado llamadosendWithTimestamp(String message)que imprima:[TIMESTAMP]y después llame asendMessage(message). Incluye también un método estático llamadoformatMessage(String message)que devuelva el mensaje convertido a mayúsculas.EmailMessenger.java: Crea una clase que implementeMessenger. Tiene un camporecipient(String). Incluye un constructor para inicializar el destinatario. ImplementasendMessage()para imprimir:Email to [recipient]: [message]. Esta clase usa el método predeterminadosendWithTimestamp()tal cual.SMSMessenger.java: Crea otra clase que implementeMessenger. Tiene un campophoneNumber(String). Incluye un constructor para inicializar el número de teléfono. ImplementasendMessage()para imprimir:SMS to [phoneNumber]: [message]. Sobrescribe el método predeterminadosendWithTimestamp()para imprimir:[URGENT]y después llamar asendMessage(message), en lugar de usar el comportamiento de marca de tiempo predeterminado.Main.java: Reúne todo para demostrar los métodos predeterminados y estáticos de las interfaces. Recibirás tres entradas: un destinatario de correo electrónico, un número de teléfono y un mensaje. Primero, usa el método estáticoMessenger.formatMessage()para dar formato al mensaje y almacenarlo. Después, crea un EmailMessenger y un SMSMessenger. Para cada mensajero, llama asendWithTimestamp()con el mensaje al que se le ha dado formato: observa cómo EmailMessenger usa la implementación predeterminada, mientras que SMSMessenger usa su versión sobrescrita.
Recibirás tres entradas: el destinatario de correo electrónico (String), el número de teléfono (String) y el mensaje (String).
Tu salida debe mostrar cuatro líneas en total: dos por cada mensajero: una línea de prefijo ([TIMESTAMP] o [URGENT]) seguida de la línea del mensaje. EmailMessenger mostrará el prefijo de marca de tiempo predeterminado, mientras que SMSMessenger mostrará su prefijo urgente personalizado. ¡Ambos usarán el mensaje al que se le ha dado formato estáticamente (en mayúsculas)!
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 emailRecipient = scanner.nextLine();
String phoneNumber = scanner.nextLine();
String message = scanner.nextLine();
// TODO: Usa Messenger.formatMessage() para formatear el mensaje y almacenarlo
// TODO: Crea un EmailMessenger con el destinatario del email
// TODO: Crea un SMSMessenger con el número de teléfono
// TODO: Llama a sendWithTimestamp() en el EmailMessenger con el mensaje formateado
// TODO: Llama a sendWithTimestamp() en el SMSMessenger con el mensaje formateado
}
}
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 IteratorPractica por tu cuenta: Compilador de Java online