Menu
Coddy logo textTech

Clases selladas (Java 17+)

Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy. Lección 52 de 87.

En Java tradicional, cuando creas una clase, cualquier otra clase puede extenderla (a menos que la marques como final). Pero ¿qué ocurre si quieres permitir la herencia, pero no de cualquiera? Java 17 introdujo las clases selladas para darte un control preciso sobre qué clases pueden extender la tuya.

Una clase sellada declara explícitamente sus subclases permitidas mediante las palabras clave sealed y permits:

sealed abstract class Shape permits Circle, Rectangle, Triangle {
    abstract double area();
}

final class Circle extends Shape {
    double radius;
    double area() { return Math.PI * radius * radius; }
}

final class Rectangle extends Shape {
    double width, height;
    double area() { return width * height; }
}

final class Triangle extends Shape {
    double base, height;
    double area() { return 0.5 * base * height; }
}

Solo Circle, Rectangle y Triangle pueden extender Shape. Cualquier otra clase que intente extenderla provocará un error de compilación.

Cada subclase permitida debe usar uno de tres modificadores: final (sin extensiones posteriores), sealed (continúa la cadena de restricciones) o non-sealed (permite extensiones sin restricciones):

sealed class Vehicle permits Car, Truck { }

final class Car extends Vehicle { }           // No se puede extender
non-sealed class Truck extends Vehicle { }    // Cualquiera puede extender Truck

Las sealed classes son especialmente potentes con la coincidencia de patrones en las expresiones switch, ya que el compilador conoce todos los subtipos posibles y puede verificar la exhaustividad. Son ideales al modelar un conjunto fijo de tipos en un dominio, como métodos de pago, tipos de respuesta o formas geométricas.

challenge icon

Desafío

Fácil

Construyamos un sistema de notificaciones que demuestre las clases selladas restringiendo qué tipos de notificaciones pueden existir en tu aplicación. Crearás una jerarquía en la que solo se permiten tipos de notificación específicos, y cada tipo gestionará la entrega de una manera diferente.

Organizarás tu código en cuatro archivos:

  • Notification.java: Crea una clase abstracta sellada que sirva como base para todas las notificaciones. Debe permitir exactamente tres subclases: EmailNotification, SMSNotification y PushNotification. Tu clase sellada necesita un campo protegido message (String), un constructor para inicializarlo, un getter para el mensaje y un método abstracto deliver() que devuelva un String describiendo cómo se envía la notificación.
  • EmailNotification.java: Crea una clase final que extienda Notification. Un EmailNotification tiene un campo privado adicional recipient (String) para la dirección de correo electrónico. Su constructor debe recibir tanto el mensaje como el destinatario y llamar al constructor principal con super(). El método deliver() debe devolver: Sending email to [recipient]: [message]
  • SMSNotification.java: Crea una clase non-sealed que extienda Notification. Esto permite que otras clases la extiendan si es necesario en el futuro. Un SMSNotification tiene un campo privado adicional phoneNumber (String). Su constructor recibe el mensaje y el número de teléfono. El método deliver() debe devolver: Sending SMS to [phoneNumber]: [message]
  • PushNotification.java: Crea una clase final que extienda Notification. Un PushNotification tiene un campo privado adicional deviceId (String). Su constructor recibe el mensaje y el ID del dispositivo. El método deliver() debe devolver: Sending push to device [deviceId]: [message]
  • Main.java: ¡Reúne tu sistema de notificaciones! Recibirás cuatro entradas: un mensaje (String), una dirección de correo electrónico (String), un número de teléfono (String) y un ID de dispositivo (String).

    Crea una notificación de cada tipo utilizando el mismo mensaje, pero su información de contacto correspondiente. Almacena las tres en un array de tipo Notification[] para demostrar el polimorfismo con clases selladas. Después, recorre el array e imprime el resultado de llamar a deliver() en cada notificación.

Recibirás cuatro entradas en este orden: mensaje, dirección de correo electrónico, número de teléfono e ID de dispositivo.

Observa cómo la clase sellada restringe la jerarquía: solo las tres clases permitidas pueden extender Notification. Cada subclase debe declararse como final, sealed o non-sealed. ¡Esto te proporciona un control completo sobre tu jerarquía de tipos y, al mismo tiempo, permite el polimorfismo!

Pruébalo tú mismo

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        // Lee las cuatro entradas
        String message = scanner.nextLine();
        String email = scanner.nextLine();
        String phoneNumber = scanner.nextLine();
        String deviceId = scanner.nextLine();
        
        // TODO: Crea uno de cada tipo de notificación usando el mismo mensaje
        // - EmailNotification con message y email
        // - SMSNotification con message y phoneNumber
        // - PushNotification con message y deviceId
        
        // TODO: Almacena las tres notificaciones en un array Notification[]
        // Esto demuestra el polimorfismo con clases selladas
        
        // TODO: Itera a través del array e imprime el resultado de deliver()
        // para cada notificació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

Practica por tu cuenta: Compilador de Java online