Curso Kotlin | #8. Null Safety

Podés consultar el curso completo desde este link que te dejo acá. Podés seguirme por LinkedIn o Twitter si querés estar al tanto de las próximas publicaciones de cada capítulo.

Null Pointer Exception, una vieja historia

Para los recién llegados, existió una antigua era en el mundo de sistemas donde usábamos Java y nuestras aplicaciones tendían a crashear bastante.

El error típico, regado por todo StackOverflow, era el famoso “Null Pointer Exception”.

Se trata de un error donde intentamos llamar a una variable, objeto, método, cualquier cosa que necesite ser instanciada o definida previamente.

Cuando corríamos la aplicación, estallaba. A veces el error era sencillo: nos habíamos olvidado de instanciarla mas arriba. Pero otras, era increíblemente complejo: venía de una lista que se volvía nula en algun momento en tiempo de ejecución, o por algun bug extraño se rompía algún componente.

En esas épocas podías evitar este error a medida que mejorabas como desarrollador, porque parte de tu trabajo se basaba en meter condicionales hasta el cansancio (tema que veremos mas adelante) y también capturar todas las posibles excepciones, incluidas las de null.

Recuerdo que una vez trabajé en una empresa donde teníamos un “Crashlytics day”, el cual se basaba en entrar a Fabric (en ese momento, posteriormente lo se integró con Firebase) y resolver uno o dos bugs que reportaban las aplicaciones en producción. Era muy típico meter un condicional que comprobara si un objeto era nulo, y asi resolver el problema.

Fue una época llena de molestias. Proteger el código es parte de programar, pero en ciertos momentos se podía volver una tarea repetitiva y no gratificante.

Null Safety, el final de la tormenta

Cuando apenas salió Kotlin a la luz para Android, uno de sus principales features anunciados fue una cosa llamada “Null Safety”.

Los que veníamos con Java no comprendíamos que estaba ocurriendo: la gente de Kotlin tenían linternas y nosotros ahí, con nuestra fogata, quemándonos a ratos.

En su conferencia, nos contaron que resolvieron el problema del Null Pointer: habían inventado una forma mas sofisticada de tratar con este problema, cambiando la forma que teníamos de ver a los valores nulos.

Nullable Strings

En la clase anterior creamos un input console del nombre y lo imprimimos:

print("Como es tu nombre? > ")
val name = readLine()
println("Tu nombre es $name")

Pero no nos percatamos de que pasaría si intentamos declarar el tipo de name. Sabemos que devuelve un String:

Acto seguido, recibimos un error: Los tipos no coinciden. Esto ocurre porque readLine no nos devuelve un String, sino un Nullable String.

Eso significa que nuestro tipo String podría ser nulo o tener un valor. El signo “?” indica esto, por lo cual vamos a modificarlo:

val name: String? = readLine()

No notamos diferencias con el método println porque si name fuera nula, imprimiría un null sin romperse. Es una de las ventajas del null safety.

No obstante, si queremos llamar a un método de String, la cosa cambia: Transformemos el texto a mayúsculas:

val nameUpper: String? = name?.toUpperCase()
println("Tu nombre es $nameUpper")

Como name es un Nullable String, necesitamos usar el “?” luego de su llamada. Es importante notar que si manejamos nullables, vamos a recibir casi siempre nullables. Por lo cual, nameUpper también lo es.

Esto es el equivalente a lo siguiente:

if(name != null) {
    val nameUpper = name.toUpperCase()
    println("Tu nombre es $nameUpper")
}

Como podemos observar, si condicionamos name, ya no hace falta usar el “?” dentro. Pero esto sería generar código innecesario, porque los nullables ya hacen esto implícitamente por nosotros.

Otro tema interesante es que si declaramos una variable nullable, podemos inicializarla en null:

var name: String? = null
println("Tu nombre es $name")
#output: Tu nombre es null

En este caso es importante declararla como “var” y no “val”, porque en el futuro necesitamos modificar su valor. Sino siempre sería null y perdería su propósito.

Esto va a ser muy útil cuando trabajemos con elementos de vista en Android, los cuales queramos reasignar en algun momento del ciclo de vida de una aplicación.

Encadenar Nullables

Este tema lo mencionamos al pasar en la clase anterior, por lo que ahora con un panorama mas amplio de los nullables, vale la pena repetirlo

val name = readLine()?.filter { it.isDigit().not() }?.toLowerCase()

En este ejemplo leemos la información en consola, quitamos los dígitos y convertimos el String a minúsculas. Como podemos observar, hay varios “?” en cada método porque algunos de estos valores pueden ser nullables.

Por ejemplo, si introducimos solo numeros, nuestro filtro no encontraría nada, por lo cual sin “?”, nuestro método “toLowerCase” nos arrojaría una excepción.

Podemos encadenar todos los nullables que quisíeramos, siempre y cuando se mantenga una coherencia y las lineas no superen el ancho establecido, y siga siendo legible.

Forzar el Null

En algunos casos muy particulares, quizá no queramos trabajar con los nullables. Supongamos que estamos totalmente seguros de que nuestro String nunca va a ser nulo.

Para asegurar esto, usamos bang bang operator o “!!” de la siguiente manera:

val name: String = readLine()!!
println("Tu nombre es $name")

Pero también podemos mantener la variable como nullable, y en el momento de usar algún método, forzarlo:

val name: String? = readLine()
println("Tu nombre es ${name!!.toUpperCase()}")

Es importante aclarar que forzar nullables rompe con las buenas prácticas que trae Kotlin a la mesa. Solo debemos usarlo en caso de no tener otra alternativa. Por ejemplo, si abusamos del “!!” y se llama a un método de un objeto nulo, la aplicación va a crashear tal como pasaba en Java.

Conclusiones

Null Safety es un tema algo complejo de entender al inicio, pero cuando lo llevamos a la práctica se vuelve muy sencillo. Recomiendo practicar esto no solo con Strings, sino también con los otros tipos de datos como Int o Boolean, por ejemplo.

¡Los espero en la siguiente clase!

¡Deja un comentario!

Artículos relacionados

Curso Kotlin | #19. Pair

A veces necesitamos relacionar dos valores y almacenarlos en una variable única, y para ello tenemos a Pair.

Curso Kotlin | #17. Proyecto: The Hero Legacy

Si empezaste esta serie desde cero y ya leíste 16 capítulos, ¡Felicidades! Empezaste a dominar los primeros conceptos fundamentales en Kotlin. Este es el primer paso de tu senda como desarrollador/a. Estamos en una instancia en donde podemos poner en práctica todo lo que aprendiste hasta ahora. Bienvenido/a al primer proyecto de la serie.

Curso Kotlin | #16. Funciones

Las funciones son procedimientos que se pueden reutilizar y nos permiten encapsular comportamientos y mejorar la lectura del código.

Curso Kotlin | #15. Excepciones

Las excepciones en Kotlin nos permiten evitar comportamientos no deseados ante problemas que se presentan en tiempo de ejecución. Por ejemplo cuando se intenta dividir por cero, castear un tipo por otro no válido, intentar agregar un valor a una lista nula, entre otros casos. Hoy vamos a ver su implementación en el código.

Curso Kotlin | #14. Rangos

Cuando hablamos de rangos, nos referimos a un intervalo de números en el sentido matemático. Podemos generar, por ejemplo, un rango que comprenda los números del 1 al 10. Hoy veremos como aplicarlo a los conceptos anteriormente aprendidos.