Nullable en Kotlin: ¿cómo trabajar con valores null sin dolor? Guía completa

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

¿Qué se esconde detrás del signo de interrogación? Sumergiéndonos en nullable en Kotlin

\n\n

¿Alguna vez has escrito en Java y te has topado con un NullPointerException en el momento más inoportuno? Si es así, sabes lo doloroso que es. Los desarrolladores de Kotlin resolvieron este problema de una vez por todas, haciendo de la null safety no solo una característica, sino una filosofía del lenguaje. Pero a los principiantes a menudo les asusta la pregunta: "In Kotlin, what is the idiomatic way to deal with nullable values, referencing or converting them?" (así es exactamente como suena la pregunta más popular en Stack Overflow).

\n\n

Hoy analizaremos todos los modismos para trabajar con nullable: desde el simple ?. hasta el astuto let y el mágico !!. Y lo más importante: miraremos bajo el capó para entender cómo funciona a nivel de bytecode. ¡Vamos allá!

\n\n

Breve introducción: ¿qué es nullable?

\n\n

En Kotlin, los tipos se dividen en dos bandos: nullable (pueden ser null) y non-nullable (no pueden). Si escribes var name: String = null, el compilador dará un error. En cambio, var name: String? = null está bien. El signo de interrogación es un marcador que dice: "Oye, compilador, sé que aquí puede haber un null, no te quejes".

\n\n

Pero, ¿cómo se trabaja con esto? Simplemente verificar si es null con un if es aburrido y no es propio de Kotlin. Veamos las herramientas reales.

\n\n

El trío salvador: ?, ?: y ?.

\n\n

Kotlin ofrece tres pilares sobre los que se sostiene todo el trabajo con nullable. Recuérdalos: te ahorrarán horas de depuración.

\n\n

1. Safe call operator (?.)

\n\n

Este es el invitado más frecuente en tu código. El operador safe call permite llamar a un método o propiedad solo si el objeto no es null. Si el objeto es null, el operador devuelve null y no se produce ninguna excepción.

\n\n
val user: User? = null\nval name: String? = user?.name  // name = null, ningún error
\n\n

Bajo el capó, esto se convierte en bytecode que realiza una verificación de null común y una llamada condicional. Sin magia, solo seguridad.

\n\n

2. Elvis operator (?:)

\n\n

Elvis es tu amigo cuando necesitas asignar un valor por defecto. Si la parte izquierda no es null, se toma esa; de lo contrario, se toma lo que está a la derecha.

\n\n
val name: String? = null\nval displayName: String = name ?: "Invitado"  // "Invitado"
\n\n

Elvis también se puede usar para salir anticipadamente de una función:

\n\n
fun getUserName(user: User?): String {\n    val name = user?.name ?: return "Anónimo"\n    return name\n}
\n\n

3. Not-null assertion (!!)

\n\n

Este es el operador rebelde. Dice: "Estoy seguro de que aquí no hay null, y estoy dispuesto a morir (es decir, lanzar un NPE) si me equivoco". Úsalo con precaución, solo cuando estés 100% seguro.

\n\n
val user: User? = getUserFromDatabase() // estamos seguros de que existe\nval name: String = user!!.name
\n\n

Bajo el capó, !! se compila en una verificación común con el lanzamiento de un NullPointerException si es null. Nada complicado, pero peligroso.

\n\n

La forma idiomática: let, run, apply y compañía

\n\n

Ahora pasamos a lo más interesante. La pregunta en Stack Overflow trata precisamente sobre esto: cómo transformar valores nullable de forma idiomática. La respuesta son las funciones de extensión de la biblioteca estándar.

\n\n

let — el rey de las transformaciones nullable

\n\n

La función let ejecuta un bloque de código solo si el objeto no es null. Dentro del bloque, el objeto está disponible como it (o se puede renombrar).

\n\n
val user: User? = getUser()\nuser?.let { \n    println("Nombre de usuario: ${it.name}")\n    // Aquí it está garantizado que no es null\n}
\n\n

Esta es la forma idiomática de ejecutar una acción solo cuando el valor no es null. Sin let, tendrías que escribir:

\n\n
if (user != null) {\n    println("Nombre de usuario: ${user.name}")\n}

Blogs

Book Recommendations