Sealed classes en Kotlin: los enums con superpoderes (KDA 28)
Antonio Leiva

Las clases selladas en Kotlin son otra de las novedades que tenemos sobre Java, y que abren otro nuevo mundo de posibilidades

Una clase sellada (o sealed class) permite representar jerarquías restringidas en las que un objeto sólo puede ser de un tipo de las dadas.

Es decir, tenemos un clase con un número específico de subclases. Lo que obtenemos al final es un concepto muy similar al de un enumerado. La diferencia radica en que en los enumerados sólo tenemos un único objeto por tipo, mientras que en las sealed classes podemos tener varios objetos de la misma clase.

Esta diferencia hace que podamos almacenar estado. Esto nos va a traer algunas ventajas que veremos en un momento, y también abre las puertas hacia ciertas ideas funcionales.

Si todo esto te apasiona tanto como a mí, te animo a que te apuntes a mi training gratuito donde te contaré todo lo que necesitas para aprende a crear tus Apps Android en Kotlin desde cero.

Cómo utilizar la sealed classes

Implementar una sealed class en realidad es muy sencillo. Vamos a poner como ejemplo un conjunto de operaciones que se pueden ejecutar sobre un entero.

La implementación sería la siguiente:

sealed class Operation {
    class Add(val value: Int) : Operation()
    class Substract(val value: Int) : Operation()
    class Multiply(val value: Int) : Operation()
    class Divide(val value: Int) : Operation()
}

Creamos una sealed class llamada Operation, que contiene cuatro tipo de operaciones: suma, resta, multiplicación y división.

Lo bueno de esto es que ahora las operaciones when nos requerirán dar opciones para todos los posibles tipos:

fun execute(x: Int, op: Operation) = when (op) {
    is Operation.Add -> x + op.value
    is Operation.Substract -> x - op.value
    is Operation.Multiply -> x * op.value
    is Operation.Divide -> x / op.value
}

Si te dejas alguna de las opciones fuera, el when se quejará y no compilará. Si las implementas todas, no necesitas sentencia else. Y en general no será muy recomendado usarlo porque así estamos seguros de que estamos haciendo lo correcto para todas ellas.

Esto también es genial en el caso de que decidas añadir una nueva operación, porque fallará en tiempo de compilación y no de ejecución. Añade un par de operaciones más: incrementar y decrementar:

sealed class Operation {
    ...
    class Increment() : Operation()
    class Decrement() : Operation()
}

Verás que ahora el compilador te avisa de que hay un problema. Sólo tienes que añadir los casos de estas nuevas operaciones:

fun execute(x: Int, op: Operation) = when (op) {
    ...
    is Operation.Increment -> x + 1
    is Operation.Decrement -> x - 1
}

Moviendo los efectos de lado a un único punto

Los efectos de lado (o side effects) son un concepto muy típico de la programación funcional. La programación funcional se basa mucho en la idea de que para una función y unos parámetros dados, el resultado debe ser siempre el mismo.

Cualquier estado que se modifique puede hacer que esto no sea verdad. Pero cualquier programa necesita modificar estados, comunicarse con elementos de entrada/salida, etc. Así que es importante minimizar las operaciones en ciertos puntos muy concretos que se puedan aislar con facilidad.

Por ejemplo, el realizar cualquier operación sobre una vista de Android se puede considerar un efecto de lado, pues se está modificando el estado de las vistas y las funciones no son conscientes de ello.

Podríamos crearnos una sealed class que nos permitiera hacer operaciones sobre nuestras vistas. Siguiendo un poco la idea del ejemplo anterior:

sealed class UiOp {
    object Show: UiOp()
    object Hide: UiOp()
    class TranslateX(val px: Float): UiOp()
    class TranslateY(val px: Float): UiOp()
}

fun execute(view: View, op: UiOp) = when (op) {
    UiOp.Show -> view.visibility = View.VISIBLE
    UiOp.Hide -> view.visibility = View.GONE
    is UiOp.TranslateX -> view.translationX = op.px
    is UiOp.TranslateY -> view.translationY = op.px
}

Fíjate que las operaciones que no tienen estado pueden ser objetos, pues no necesitamos distintas instancias.

Ahora puedes crearte un objeto Ui  que acumule todas las operaciones de interfaz que queremos hacer sobre una vista, pero que no las ejecute hasta el momento que queramos.

Tendremos una descripción de lo que queremos hacer, y luego podremos crear un componente que las ejecute:

class Ui(val uiOps: List = emptyList()) {
    operator fun plus(uiOp: UiOp) = Ui(uiOps + uiOp)
}

La clase Ui almacena un listado de operaciones, y especifica un operador de suma que nos ayudará a que quede todo un poco más limpio y sencillo de leer. Ahora podemos especificar el listado de operaciones que queremos realizar:

val ui = Ui() +
        UiOp.Show +
        UiOp.TranslateX(20f) +
        UiOp.TranslateY(40f) +
        UiOp.Hide

run(view, ui)

Y posteriormente ejecutarlo. Aquí solo estoy utilizando una función run, pero esto podría ser a su vez un objeto.

fun run(view: View, ui: Ui) {
    ui.uiOps.forEach { execute(view, it) }
}

Imagina la potencia que te da esto. Ahora mismo lo único que se hace es ejecutar las operaciones secuencialmente.

Pero podrías crearte un objeto que ejecutase animaciones para cada una de las operaciones, y sólo cambiando eso, el resto del programa funcionaría de la misma forma.

Conclusión

El concepto de las sealed classes es muy sencillo, pero es la base de un montón de ideas nuevas a las que hay que acostumbrarse si uno no conoce mucho sobre programación funcional.

Yo mismo reconozco que aún no soy capaz de sacarle toda la potencia a esto por mis limitaciones en el mundo funcional.

¿Te gustaría comenzar hoy a dar el siguiente paso? Te recomiendo que entres a mi training gratuito aquí.

Quizá también te interese…

Kotlin 1.5.0 : Las 5 novedades que puedes empezar a usar hoy

Kotlin 1.5.0 : Las 5 novedades que puedes empezar a usar hoy

Kotlin 1.5.0 ya está aquí, y como siempre trae una serie de novedades que te van a interesar muchísimo. Cabe destacar que a partir de ahora, de acuerdo las nuevas versiones de Kotlin se lanzarán cada 6 meses, independientemente de las nuevas funcionalidades que...

¿Qué es Kotlin y para qué sirve?

¿Qué es Kotlin y para qué sirve?

Kotlin es un lenguaje de programación de código abierto creado por JetBrains que se ha popularizado gracias a que se puede utilizar para programar aplicaciones Android. Pero si has llegado hasta aquí pensando que Kotlin solo se puede usar en Android, lo que te voy a...

2 formas de recolectar Flows en la UI que SÍ funcionan

2 formas de recolectar Flows en la UI que SÍ funcionan

En la serie de artículos sobre Programación Reactiva con Flow hemos visto muchos conceptos, y hemos aprendido cómo aplicarlos al desarrollo Android. Pero hay algo que no hemos hecho del todo bien. Esto es la recolección de Flows desde la Activity (o el Fragment, en...

0 comentarios

Enviar un comentario

Los datos personales que proporciones a través de este formulario quedarán registrados en un fichero de Antonio Leiva Gordillo, con el fin de gestionar los comentarios que realizas en este blog. La legitimación se realiza a través del consentimiento de la parte interesada. Si no se acepta, no podrás comentar en este blog. Los datos que proporciona solo se utilizan para evitar el correo no deseado y no se usarán para nada más. Puede ejercer los derechos de acceso, rectificación, cancelación y oposición en contacto@devexperto.com.

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Acepto la política de privacidad *