Contenido del curso

Sealed classes em Kotlin para estados

Resumen

Las sealed classes en Kotlin te permiten modelar jerarquías cerradas de estados con herencia controlada, ideales para representar el ciclo de vida de un correo, vistas de UI o flujos finitos. Aquí aprenderás cómo declararlas, compartir propiedades entre subclases y consumirlas con when exhaustivo.

¿Qué son las sealed classes y por qué importan en Kotlin?

Una sealed class es una clase base que limita qué subclases pueden heredar de ella. Tú defines de antemano todos los estados posibles y el compilador te obliga a manejarlos.

En el ejemplo del curso [01:00], modelamos EmailStatus como la clase principal y describimos los estados reales por los que pasa un correo: creado, enviado, fallido, en borrador o programado. Cada uno de esos estados se convierte en una subclase con sus propios datos.

¿Qué es una sealed class en Kotlin? Es una clase base que restringe la herencia a un conjunto finito y conocido de subclases declaradas dentro de su mismo archivo. Sirve para modelar estados cerrados como los de una UI.

¿Cómo declarar una sealed class con subclases tipadas?

La palabra clave sealed declara la relación controlada. Cada subclase extiende de la clase base y puede traer sus propios parámetros.

En el ejercicio [02:30] se construye algo así:

kotlin sealed class EmailStatus(val statusId: Int) { class Sent(val sentTo: String) : EmailStatus(0) class Failed(val error: String) : EmailStatus(1) data object Draft : EmailStatus(2) class Scheduled(val time: String) : EmailStatus(3) }

Fíjate en tres detalles importantes:

  • Sent recibe un sentTo con el correo destino.
  • Failed recibe un error como string.
  • Scheduled recibe el time de envío.

¿Cuándo usar data object en lugar de data class?

Cuando un estado no carga ningún parámetro, como Draft, no tiene sentido usar una data class. Ahí entra el data object, un concepto que el instructor introduce alrededor de [03:40] como una forma limpia de representar un estado vacío sin datos asociados.

¿Cómo compartir propiedades entre subclases?

Puedes declarar un constructor en la clase sellada y pasar valores comunes desde cada subclase. En la clase se usa statusId: Int como identificador compartido [04:30]: Sent lleva 0, Failed lleva 1, Draft lleva 2 y Scheduled lleva 3.

Esa propiedad vive en la clase base, así que cualquier instancia de EmailStatus la expone sin necesidad de hacer casting.

¿Cómo consumir una sealed class con when exhaustivo?

La forma natural de leer una sealed class es con when. El compilador exige que cubras todas las ramas, igual que con los enums.

En el ejemplo [06:30] se simula una función procesarEmail que recibe un entero y devuelve un EmailStatus:

kotlin fun procesarEmail(estado: Int, email: Email): EmailStatus { return when (estado) { 0 -> EmailStatus.Sent(email.remitente) 1 -> EmailStatus.Failed("Estado de error") 2 -> EmailStatus.Draft 3 -> EmailStatus.Scheduled(Date().time.toString()) else -> EmailStatus.Failed("Estado de error") } }

Luego, al recibir el resultado, otro when parametrizado decide qué mostrar al usuario:

kotlin when (emailStatus) { is EmailStatus.Sent -> println("Estado enviado a ${emailStatus.sentTo}") is EmailStatus.Failed -> println("Estado fallido ${emailStatus.error}") is EmailStatus.Draft -> println("Estado borrador") is EmailStatus.Scheduled -> println("Programado para ${emailStatus.time}") }

¿Para qué sirve el operador is en sealed classes?

El operador is pregunta si la instancia pertenece a una subclase concreta. Cuando esa verificación es true, dentro de esa rama Kotlin hace smart cast y te deja acceder a las propiedades específicas de esa subclase, como sentTo en Sent o error en Failed [09:50].

Fuera del when, sin verificar el tipo, solo puedes acceder a la propiedad compartida statusId.

¿Qué diferencia hay entre sealed class y enum class? Un enum representa valores constantes sin estado. Una sealed class permite que cada subclase cargue sus propios datos y comportamiento, manteniendo la exhaustividad en when.

¿Cómo practicar con un ejercicio de EmailFolder?

El reto propuesto en [11:30] es modelar las carpetas de un correo con una sealed class llamada EmailFolder y una propiedad compartida count.

La solución esperada incluye:

  • Un data object Inbox con count = 10.
  • Una data class Spam con un parámetro senderDomain: String y count = 5.
  • Un data object Sent con count = 3.

kotlin sealed class EmailFolder(val count: Int) { data object Inbox : EmailFolder(10) data class Spam(val senderDomain: String) : EmailFolder(5) data object Sent : EmailFolder(3) }

Este patrón es justo lo que necesitas cuando modelas estados de UI o eventos finitos. Y si lo que quieres modelar son acciones del usuario sobre la interfaz, ahí entran las sealed interfaces, el siguiente paso natural.

¿Cómo aplicarías una sealed class en tu propio proyecto? Cuéntame en los comentarios qué estados estás intentando modelar.