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é pasa 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 utilizando las palabras clave sealed y permits:

sealed 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 causará un error de compilación.

Cada subclase permitida debe usar uno de tres modificadores: final (sin extensión adicional), sealed (continúa la cadena de restricción) o non-sealed (se abre para una extensión 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 clases selladas son particularmente potentes con la coincidencia de patrones (pattern matching) en 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

¡Vamos a construir un sistema de notificaciones que demuestre las clases selladas (sealed classes) restringiendo qué tipos de notificaciones pueden existir en tu aplicación! Crearás una jerarquía donde solo se permiten tipos de notificación específicos, y cada tipo maneja la entrega de manera diferente.

Organizarás tu código en cuatro archivos:

  • Notification.java: Crea una clase abstracta sellada (sealed abstract class) 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 de Notification. Una 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, llamando al constructor padre con super(). El método deliver() debe devolver: "Sending email to " + recipient + ": " + message
  • SMSNotification.java: Crea una clase non-sealed que extienda de Notification; esto permite que otras clases la extiendan si es necesario en el futuro. Una 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 de Notification. Una 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: ¡Une todo 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 usando el mismo mensaje pero con su respectiva información de contacto. Almacena las tres en un arreglo de tipo Notification[] para demostrar el polimorfismo con clases selladas. Luego, itera a través del arreglo e imprime el resultado de llamar a deliver() en cada notificación.

Recibirás cuatro entradas en 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 da un control total sobre tu jerarquía de tipos mientras sigues permitiendo el polimorfismo!

Hoja de referencia

Una clase sellada (sealed class) restringe qué clases pueden extenderla utilizando las palabras clave sealed y permits:

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

Solo las clases enumeradas en permits pueden extender la clase sellada. Cualquier otra clase que intente extenderla provocará un error de compilación.

Cada subclase permitida debe utilizar uno de tres modificadores:

  • final - evita extensiones adicionales
  • sealed - continúa la cadena de restricción con sus propias subclases permitidas
  • non-sealed - abre la jerarquía para una extensión 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 clases selladas son útiles para modelar un conjunto fijo de tipos en un dominio, como métodos de pago, tipos de respuesta o formas geométricas. Funcionan bien con la coincidencia de patrones (pattern matching) en expresiones switch, ya que el compilador conoce todos los subtipos posibles.

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 una notificación de cada tipo usando el mismo message
        // - EmailNotification con message y email
        // - SMSNotification con message y phoneNumber
        // - PushNotification con message y deviceId
        
        // TODO: Almacena las tres notificaciones en un arreglo Notification[]
        // Esto demuestra el polimorfismo con sealed classes
        
        // TODO: Itera a través del arreglo 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