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 hace más flexibles.
Un método predeterminado proporciona una implementación directamente en la interfaz utilizando 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() de forma gratuita. Esto es particularmente útil para añadir nuevos métodos a interfaces existentes sin romper las clases que ya las 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 utilizando 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 de interfaz estáticos no pueden ser sobrescritos y no son heredados por las clases que los implementan. 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ácilVamos a construir 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 integrada 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]seguido de la llamada asendMessage(message). También incluye 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 utiliza 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]seguido de la llamada asendMessage(message)en lugar de usar el comportamiento de marca de tiempo predeterminado.Main.java: Reúne todo para demostrar tanto los métodos de interfaz predeterminados como los estáticos. 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 formatear el mensaje y almacenarlo. Luego crea un EmailMessenger y un SMSMessenger. Para cada mensajero, llama asendWithTimestamp()con el mensaje formateado; observa cómo el EmailMessenger usa la implementación predeterminada mientras que SMSMessenger usa su versión sobrescrita.
Recibirás tres entradas: el destinatario del correo electrónico (String), el número de teléfono (String) y el mensaje (String).
Tu salida debería mostrar cuatro líneas en total, dos para cada mensajero. El EmailMessenger mostrará el prefijo de marca de tiempo predeterminado, mientras que el SMSMessenger mostrará su prefijo urgente personalizado. ¡Ambos usarán el mensaje formateado estáticamente (en mayúsculas)!
Hoja de referencia
A partir de Java 8, las interfaces pueden incluir métodos predeterminados y métodos estáticos además de los métodos abstractos.
Los métodos predeterminados proporcionan una implementación directamente en la interfaz utilizando 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();
}
}Los métodos estáticos en las interfaces pertenecen a la interfaz misma y se llaman utilizando el nombre de la interfaz. No se pueden sobrescribir y no son heredados por las clases que las implementan:
public interface MathOperations {
static int add(int a, int b) {
return a + b;
}
}
// Uso
int result = MathOperations.add(5, 3);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.
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: Usar Messenger.formatMessage() para formatear el mensaje y guardarlo
// TODO: Crear un EmailMessenger con el destinatario del correo electrónico
// TODO: Crear un SMSMessenger con el número de teléfono
// TODO: Llamar a sendWithTimestamp() en el EmailMessenger con el mensaje formateado
// TODO: Llamar 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 Iterator