Código limpio en Kotlin: reglas que te ahorrarán dolores de cabeza

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

Introducción: Por qué el "código limpio" no va de estética



Cuando empiezas a escribir en Kotlin, parece que el propio lenguaje ya resuelve la mitad de los problemas. Sin código repetitivo como en Java. Tienes `data class`, lambdas, corrutinas. Pero si simplemente acumulas código "sobre la marcha", en un mes no podrás entenderlo tú mismo. Y si un equipo trabaja en el proyecto, tus colegas te odiarán (o tú a ellos).



El código limpio en Kotlin no va de tabulaciones frente a espacios. Se trata de reducir la carga cognitiva, hacer la lógica transparente y evitar errores que se esconden en construcciones "cómodas". En este artículo te mostraré técnicas concretas: desde trabajar con nullable hasta usar scope functions y sealed classes. Sin rodeos, solo práctica.



1. Null Safety: cómo no multiplicar "?" y "!!"



Kotlin se enorgullece de su sistema de tipos, pero muchos escriben como si fuera Java con "azúcar sintáctico". El principal antipatrón son las cadenas de `?.let` y los `!!` de pánico.



Malo (la legibilidad sufre):

user?.address?.city?.let { city ->    println(city)} ?: run {    println("No city")}


Bueno (elegante y limpio):

val city = user?.address?.cityprintln(city ?: "No city")


Usa `!!` solo si estás 100% seguro de que el valor no es null (por ejemplo, en pruebas o después de una comprobación con `if`). En producción, es una granada. Si necesitas convertir un nullable en no nulo y manejar ambos casos, usa when o el operador Elvis.



2. Scope Functions: `apply`, `run`, `with`, `let`, `also` — ¿cuándo usar cada una?



Son una herramienta potente, pero a menudo se abusa de ellas. La regla de oro: no anides scope functions unas dentro de otras. Si tienes `list.apply { ... }.let { ... }`, estás escribiendo código espagueti.



Reglas de selección:

  • apply — para configurar un objeto (devuelve el propio objeto). Ideal para constructores.
  • let — para ejecutar una acción con un valor no nulo (a menudo en combinación con `?.`).
  • run — cuando necesitas calcular un valor y devolver un resultado.
  • with — para agrupar llamadas a un mismo objeto sin devolver nada.
  • also — para efectos secundarios (registro, validación).


Ejemplo limpio:

data class User(var name: String, var age: Int)

fun createUser(): User = User("Unknown", 0).apply { name = "Alice" age = 30}


No escribas user?.let { it.name } — es redundante. Simplemente usa user?.name.



3. Extension Functions: no multipliques clases de utilidades



En Java escribirías StringUtils.capitalize(str). En Kotlin — str.capitalize(). Las funciones de extensión son una forma de "completar" una clase ajena sin heredar de ella.



Ejemplo de extensión útil:

fun String.isValidEmail(): Boolean {    return this.contains("@") && this.contains(".")}


Pero no te excedas. Si una función de extensión se usa solo en un lugar, quizás debería ser privada. Y no hagas extensiones sobre `Any` — eso rompe el sistema de tipos.



4. Sealed Classes y When: tu herramienta principal en lugar de if-else



Cuando tienes un conjunto finito de estados (por ejemplo, el resultado de una solicitud API), usa sealed class. Esto garantiza que has manejado todas las variantes.



Enfoque correcto:

sealed class NetworkResult<out T> {    data class Success<T>(val data: T) : NetworkResult<T>()    data class Error(val message: String, val code: Int) : NetworkResult<Nothing>()    object Loading : NetworkResult<Nothing>()}

fun handle(result: NetworkResult<String>) { when (result) { is NetworkResult.Success -> println("Data: ${result.data}") is NetworkResult.Error -> println("Error: ${resu

Blogs

Book Recommendations