Guía Detallada sobre Clases e Interfaces Selladas (Sealed Classes and Interfaces) en Java

Introducción

En Java, sealed classes y sealed interfaces (clases e interfaces selladas) son características introducidas en la versión Java 15 como parte del JEP 409, que permiten controlar y restringir la jerarquía de clases o interfaces que pueden implementarlas o extenderlas. Esta característica ofrece un control más preciso sobre la herencia y mejora la seguridad y el mantenimiento del código.

El objetivo de esta guía es explicar los conceptos básicos de las clases e interfaces selladas, cómo usarlas, y proporcionar ejemplos sencillos para ayudarte a entender cómo funcionan. Al final, también se incluirán ejercicios prácticos para que puedas afianzar tu comprensión.

1. ¿Qué es una Clase Sellada (Sealed Class)?

Una clase sellada es una clase que restringe qué otras clases pueden extenderla. Cuando se define una clase como sellada, solo se permite que clases específicas la extiendan. Esto es útil cuando deseas limitar la herencia a un conjunto controlado de clases para mantener la consistencia del diseño de tu aplicación.

Sintaxis de una Clase Sellada

La palabra clave sealed se usa para declarar una clase sellada. Además, debes especificar qué clases pueden extender esta clase utilizando la palabra clave permits.

Ejemplo:

// Definición de una clase sellada
public sealed class Animal permits Dog, Cat {
    // Miembros de la clase
    public void hacerSonido() {
        System.out.println("Sonido genérico de un animal");
    }
}

// Clase Dog extiende Animal
public final class Dog extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("Guau");
    }
}

// Clase Cat extiende Animal
public final class Cat extends Animal {
    @Override
    public void hacerSonido() {
        System.out.println("Miau");
    }
}

// Clase Rabbit que no puede extender Animal
// public class Rabbit extends Animal {} // Esto generaría un error de compilación

Explicación:

Restricciones:

  1. Solo las clases mencionadas en el permits pueden extender la clase sellada.
  2. Las clases que extienden una clase sellada deben ser final, sealed o non-sealed.

2. ¿Qué es una Interfaz Sellada (Sealed Interface)?

Una interfaz sellada funciona de manera similar a una clase sellada, pero en lugar de restringir la herencia de clases, restringe qué interfaces o clases pueden implementarla.

Sintaxis de una Interfaz Sellada

Se declara una interfaz como sellada usando la palabra clave sealed, y luego se especifican las clases o interfaces que pueden implementarla utilizando permits.

Ejemplo:

// Definición de una interfaz sellada
public sealed interface Forma permits Circulo, Cuadrado {
    double area();
}

// Clase Circulo implementa Forma
public final class Circulo implements Forma {
    private double radio;

    public Circulo(double radio) {
        this.radio = radio;
    }

    @Override
    public double area() {
        return Math.PI * radio * radio;
    }
}

// Clase Cuadrado implementa Forma
public final class Cuadrado implements Forma {
    private double lado;

    public Cuadrado(double lado) {
        this.lado = lado;
    }

    @Override
    public double area() {
        return lado * lado;
    }
}

// Clase Triangulo que no puede implementar Forma
// public class Triangulo implements Forma {} // Esto generaría un error de compilación

Explicación:

Restricciones:

  1. Solo las clases o interfaces mencionadas en permits pueden implementar la interfaz sellada.
  2. Las clases o interfaces que implementan una interfaz sellada deben ser final, sealed o non-sealed.

3. Usos de Clases e Interfaces Selladas

Ventajas:

  1. Control de la herencia: Puedes restringir qué clases pueden extender o qué interfaces pueden implementar, lo que te da mayor control sobre la estructura del código.
  2. Seguridad en el diseño: Al limitar la herencia, puedes evitar que otras clases no deseadas modifiquen el comportamiento de tus clases o interfaces, mejorando la seguridad del diseño.
  3. Patrones de diseño: Las clases e interfaces selladas son útiles cuando implementas patrones como el Patrón de Estado o Patrón de Estrategia, donde solo un conjunto limitado de implementaciones es válido.

Ejemplo de uso práctico: Patente de vehículos

Imagina que estás modelando un sistema para una patente de vehículos. Tienes una clase base Vehiculo que solo puede ser extendida por clases Coche, Motocicleta, y Camion, para asegurarte de que solo esos tipos de vehículos sean parte de tu sistema.

public sealed class Vehiculo permits Coche, Motocicleta, Camion {
    // Código común para vehículos
}

public final class Coche extends Vehiculo {
    // Implementación específica de un coche
}

public final class Motocicleta extends Vehiculo {
    // Implementación específica de una motocicleta
}

public final class Camion extends Vehiculo {
    // Implementación específica de un camión
}

Este diseño garantiza que solo Coche, Motocicleta y Camion sean considerados tipos válidos de Vehiculo.

4. non-sealed y final en Clases e Interfaces Selladas

En las clases o interfaces selladas, puedes usar la palabra clave non-sealed en clases que extienden una clase sellada para permitir que se extiendan libremente. Esto se usa cuando quieres que una clase sea parte de la jerarquía pero no quieres seguir restringiendo sus descendientes.

Ejemplo de non-sealed:

public sealed class Animal permits Perro, Gato {}

public non-sealed class Perro extends Animal {}
public class Bulldog extends Perro {}  // Bulldog puede extender Perro

En este caso, Perro es non-sealed, por lo que puede ser extendido por otras clases como Bulldog.

5. Ejercicios Propuestos

Ejercicio 1: Clases Selladas

Define una clase sellada Producto que solo pueda ser extendida por las clases Electrodomestico y Alimento. Crea una jerarquía de clases que demuestre su uso.

Ejercicio 2: Interfaces Selladas

Crea una interfaz sellada llamada Operable que solo pueda ser implementada por Computadora y Smartphone. Define un método encender() y crea las clases correspondientes.

Ejercicio 3: Uso de non-sealed

Crea una clase sellada Vehiculo con subclases Coche y Bicicleta. Usa la palabra clave non-sealed en una subclase de Coche para permitir más herencia.

Conclusión

Las clases e interfaces selladas en Java proporcionan un control robusto sobre la jerarquía de herencia, permitiendo definir un conjunto limitado de clases o interfaces que pueden extender o implementar una clase o interfaz sellada. Esto ayuda a evitar modificaciones no deseadas y mejora la seguridad del diseño del software. Ahora que comprendes los conceptos fundamentales de las clases e interfaces selladas, puedes utilizar esta característica para mejorar la estructura y la mantenibilidad de tus aplicaciones Java.