¿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).
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á!
Breve introducción: ¿qué es nullable?
\n\nEn 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".
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.
El trío salvador: ?, ?: y ?.
\n\nKotlin ofrece tres pilares sobre los que se sostiene todo el trabajo con nullable. Recuérdalos: te ahorrarán horas de depuración.
\n\n1. Safe call operator (?.)
\n\nEste 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\nval user: User? = null\nval name: String? = user?.name // name = null, ningún error\n\nBajo 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\n2. Elvis operator (?:)
\n\nElvis 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\nval name: String? = null\nval displayName: String = name ?: "Invitado" // "Invitado"\n\nElvis también se puede usar para salir anticipadamente de una función:
\n\nfun getUserName(user: User?): String {\n val name = user?.name ?: return "Anónimo"\n return name\n}\n\n3. Not-null assertion (!!)
\n\nEste 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\nval user: User? = getUserFromDatabase() // estamos seguros de que existe\nval name: String = user!!.name\n\nBajo el capó, !! se compila en una verificación común con el lanzamiento de un NullPointerException si es null. Nada complicado, pero peligroso.
La forma idiomática: let, run, apply y compañía
\n\nAhora 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\nlet — el rey de las transformaciones nullable
\n\nLa 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).
val user: User? = getUser()\nuser?.let { \n println("Nombre de usuario: ${it.name}")\n // Aquí it está garantizado que no es null\n}\n\nEsta es la forma idiomática de ejecutar una acción solo cuando el valor no es null. Sin let, tendrías que escribir:
if (user != null) {\n println("Nombre de usuario: ${user.name}")\n}
Blogs
Book Recommendations
Leer también
Swift para aprendizaje automático: posibilidades y limitaciones