Rechaza el doble bang en Kotlin generado
Cada doble bang en Kotlin generado por IA es un crash esperando llegar a producción. El operador !! convierte cualquier valor a un tipo no anulable — y si el valor es null en ese momento, lanza una NullPointerException ahí mismo. La documentación de Kotlin lista !! como una de solo tres formas de provocar una NPE en código Kotlin puro. Cuando un asistente de IA lo esparce por el código generado, no está afirmando conocimiento: está silenciando al compilador porque es el camino más corto hacia código que compila.
Por qué el código generado ama !!
Los generadores de código optimizan para "compila al primer intento". Frente a un tipo anulable, las dos opciones honestas — propagar la anulabilidad o manejar la rama null — cuestan más código. !! cuesta dos caracteres. El mismo atajo aparece como el campo anulable de fase de inicialización: un var user: User? = null que "seguro se asigna antes de que alguien lo lea". Esa promesa vive en un comentario o en la cabeza del autor, no en el sistema de tipos, así que cada sitio de lectura o maneja un null que "no puede pasar" o le aplica doble bang. En ambos casos, la ausencia pasó de ser un hecho en tiempo de compilación a una apuesta en tiempo de ejecución.
Arreglo 1: haz la ausencia explícita y manéjala
Si un valor puede genuinamente faltar, mantén el tipo anulable y usa los operadores que el lenguaje te da:
// AI draft — deferred crash
val name = response.user!!.name
// Explicit — compiler-checked
val name = response.user?.name ?: "anonymous"
La llamada segura ?. devuelve null en lugar de lanzar una excepción; el operador Elvis ?: provee el valor por defecto. Las cadenas componen: bob?.department?.head?.name es null si cualquier eslabón es null — ninguna rama de esa expresión puede lanzar una NPE.
Arreglo 2: modela estados cerrados con resultados sealed
Cuando "null" representa un estado — aún no cargado, cargado, falló — un campo anulable es la herramienta equivocada. Una jerarquía sealed nombra cada estado, y como todas las subclases directas de una clase sealed se conocen en tiempo de compilación, el when sobre ella es exhaustivo sin else:
sealed interface LoadState {
data object Loading : LoadState
data class Ready(val user: User) : LoadState
data class Failed(val cause: Exception) : LoadState
}
fun render(s: LoadState) = when (s) {
LoadState.Loading -> showSpinner()
is LoadState.Ready -> showUser(s.user)
is LoadState.Failed -> showError(s.cause)
// no else — the compiler proves every case is covered
}
Agrega un cuarto estado el próximo mes y cada when que lo olvidó se vuelve un error de compilación, no una alerta a las 3 a.m. Ese es el beneficio: el compilador te obliga a manejar el caso vacío, y clases enteras de crashes se vuelven inalcanzables.
Reglas prácticas para revisión
| Ves en el diff | Pide en su lugar |
|---|---|
foo!!.bar | foo?.bar + fallback con ?:, o arreglar el tipo aguas arriba |
var x: T? = null asignado "después" durante init | Inyección por constructor, o un estado sealed Loading/Ready |
lateinit var leído antes de una asignación garantizada | Guard con ::x.isInitialized — o reestructurar el orden de init |
when sobre un tipo sealed con rama else | when exhaustivo, sin else, para que casos nuevos rompan el build |
| Valor sin anotar de una API Java | Tipo Kotlin explícito en la firma pública |
Notas para usuarios avanzados
lateinitno es un pase libre. Leerlo antes de asignarlo lanzaUninitializedPropertyAccessException— un nombre más amable para el mismo crash. Úsalo solo donde un framework genuinamente asigna antes de usar (DI, setup de tests), y protege carreras raras con::prop.isInitialized.- Los platform types son doble bangs ocultos. Los valores que vienen de Java tienen anulabilidad desconocida; las convenciones de código de Kotlin indican que las funciones públicas y propiedades inicializadas desde platform types deben declarar su tipo Kotlin explícito. Hazlo en la frontera, una vez, en lugar de
!!en diez sitios de llamada. - Haz el veto ejecutable. Dile a tu asistente en sus instrucciones de proyecto: "Never emit
!!; prefer?./?:or sealed results." Los generadores siguen restricciones declaradas mucho mejor de lo que infieren el buen gusto — y tus revisores dejan de repetir el mismo comentario.
Recursos
- Null safety — documentación de Kotlin:
?.,?:y por qué!!es una de las tres causas de NPE - Sealed classes and interfaces — documentación de Kotlin: jerarquías cerradas y
whenexhaustivo - Late-initialized properties — documentación de Kotlin:
lateinit, su excepción eisInitialized - Coding conventions: platform types — documentación de Kotlin
¿Construyes con código generado por IA? Yeda AI diseña, audita y entrega sistemas LLM de producción.