Skip to main content

Declaración de variables

Las variables son uno de los conceptos más básicos de un lenguaje de programación, pero en Kotlin no se limitan a ser simples contenedores de datos: son propiedades que pueden incluir lógica de acceso, validación y encapsulamiento.

Este enfoque convierte incluso a las declaraciones más simples en herramientas poderosas de diseño, especialmente cuando estás creando bibliotecas reutilizables.

En esta lección aprenderás a:

  • Declarar variables con val y var, y entender las diferencias entre referencias inmutables y mutables.
  • Encapsular correctamente el acceso a los datos usando getters y setters personalizados.
  • Proteger las invariantes internas de tus estructuras, sin perder expresividad hacia quienes usan tu API.

Todo esto con un enfoque práctico y centrado en buenas prácticas de diseño, que te ayudarán a escribir código claro, seguro y mantenible.

📦 Declaración de variables

En Kotlin puedes declarar variables de dos formas:

  • valde solo lectura: su valor no puede reasignarse una vez inicializado.
  • varlectura y escritura: su valor puede cambiar después de la declaración.
Sintaxis general
val o var nombreVariable: Tipo = valor

Inferencia de tipos

En variables locales o privadas, puedes omitir el tipo si el compilador puede inferirlo a partir del valor asignado.
Esto mejora la legibilidad sin perder seguridad de tipos.

Sin embargo, en bibliotecas es recomendable declarar explícitamente el tipo de las propiedades y funciones públicas,
ya que esto hace más robusta la API, facilita el mantenimiento y evita cambios accidentales en la firma pública.

🔒 Sólo lectura (val)

Usa val cuando la referencia de la variable no cambiará después de su asignación.
Una vez inicializada, no podrás volver a asignarle otro valor.

Variables de solo lectura
val master = "Goomoonryong"
master = "Yi Shi-Woon" // 🔥 Error: no se puede reasignar un 'val'

val student: String
student = "Haje Kang" // ✅ Correcto: asignación diferida (una sola vez)
student = "Shi-Ho Lee" // 🔥 Error: la referencia no puede cambiar

A lo largo del curso nos referiremos a estas variables como referencias inmutables o propiedades de solo lectura: su referencia no puede cambiar una vez asignada.

Esto no implica que el valor al que apuntan sea inmutable.
Por ejemplo, una variable val puede referirse a una lista mutable: aunque no puedas cambiar la referencia, sí puedes modificar el contenido de la lista.

Más adelante hablaremos de las constantes en tiempo de compilación, declaradas con const val, cuyo valor debe conocerse en tiempo de compilación.
Por ahora, basta con distinguir entre referencias mutables (var) y referencias inmutables (val).

🔓 Lectura y escritura (var)

Usa var cuando necesites modificar el valor o cambiar la referencia de una variable después de declararla:

Variables de lectura y escritura
var technique = "Black Heaven & Earth"
technique = "Soul-Crushing Strike" // ✅ Correcto: se cambia la técnica referenciada

var energy = 10
energy = energy + 5 // ✅ Correcto: suma explícita
energy += 5 // ✅ Equivalente más conciso
energy++ // ✅ Incremento en 1

¡Prefiere val!

Siempre que sea posible, prefiere val en lugar de var.
La inmutabilidad no solo mejora la legibilidad y el mantenimiento del código, sino que también facilita el razonamiento formal sobre su comportamiento.
Usa var únicamente cuando realmente necesites que el valor de una variable cambie a lo largo del tiempo.

Expresividad e intención: mejor con val que con var
// ✅ Mejor con val: expresa intención clara, el valor no cambia
val basePower = 42
val bonus = 8
val total = basePower + bonus

// 🔶 Menos claro con var: sugiere que el valor podría cambiar
var total = 42
total += 8

En el primer caso, total es el resultado de una operación fija y predecible.
En el segundo, el uso de var puede dar la impresión de que total seguirá cambiando más adelante, aunque no sea así.

🔄 ¿Cuándo usar var?

Aunque val debe ser tu primera opción, hay situaciones justificadas donde el uso de var es adecuado:

  • Cuando una variable cambia como parte del estado interno de una clase mutable.
  • Si estás acumulando resultados a lo largo de un bucle o una función.
  • Al implementar algoritmos imperativos donde el cambio de estado es más natural o legible.
  • En tests o scripts donde la claridad y simplicidad pueden pesar más que la inmutabilidad.

🧱 Propiedades en Kotlin: más que campos

En Kotlin, val y var no solo declaran campos como en otros lenguajes:
en realidad definen propiedades con acceso controlado a través de getters y setters.

Esto permite encapsular lógica sin perder expresividad:

Propiedades en Kotlin
val soloLectura: Tipo = inicializador
get() = campoPersonalizado // Getter implícito o redefinido

var lecturaEscritura: Tipo = inicializador
get() = campoPersonalizado
set(value) {
// lógica adicional antes de asignar
field = value
}

En el contexto de bibliotecas, esto permite definir APIs que exponen información de forma segura, mientras internamente conservan flexibilidad para evolucionar o proteger invariantes.

🧪 Ejemplo práctico: Exponer listas inmutables con un campo de respaldo

Cuando desarrollas una biblioteca, es común tener estructuras internas que deben mantenerse mutables, pero que no deben exponerse directamente a quienes consumen tu API.
Kotlin permite resolver esto mediante un campo de respaldo:

Battle.kt
private val _party: MutableList<String> = mutableListOf("Balthier", "Vaan")

val party: List<String>
get() = _party

Este patrón es común en bibliotecas bien diseñadas: exponer lo mínimo necesario mientras se protegen las invariantes internas.

Conversión implícita de mutabilidad

En Kotlin, cuando una función o propiedad devuelve una MutableList<T> pero declara su tipo como List<T>, el compilador permite esta conversión implícitamente.

Esto es útil al desarrollar bibliotecas: puedes mantener una estructura mutable internamente (MutableList<String>) y exponer solo una vista inmutable (List<String>) al exterior, sin necesidad de copiar los datos o envolver la lista manualmente.

🧪 Ejemplo práctico: Encapsular lógica de asignación en el setter

Cuando diseñamos una biblioteca, puede ser necesario que ciertos valores no se asignen directamente, sino que pasen por validaciones o transformaciones. Esto se logra mediante un setter personalizado.

Config.kt
var version: String = "1.0.0"
set(value) {
require(Regex("""\d+\.\d+\.\d+""").matches(value)) {
"Version must be in the format X.Y.Z where X, Y, and Z are integers."
}
field = value
}

Este patrón permite crear APIs intuitivas pero seguras, combinando accesibilidad con control sobre las invariantes internas.

🧩 Ejercicio de cierre: Controlar el nivel de dificultad desde el motor

Supón que estás diseñando una biblioteca para un motor de juego.
Quieres exponer el nivel de dificultad actual para que otras personas puedan consultarlo, pero solo el motor debe poder modificarlo.

Declara una propiedad difficultyLevel que:

  • Sea de tipo String.
  • Permita valores como "easy", "normal" o "hard".
  • Pueda leerse públicamente desde cualquier parte.
  • Solo pueda modificarse desde dentro del módulo, y con validación.
Solución
Engine.kt
var difficultyLevel: String = "normal"
private set(value) {
require(value in listOf("easy", "normal", "hard")) {
"Difficulty level must be one of: easy, normal, hard."
}
field = value
}

🎯 Conclusiones

A lo largo de esta lección aprendiste a declarar variables en Kotlin, diferenciando claramente entre referencias inmutables (val) y mutables (var). Exploramos cuándo conviene usar cada una y cómo Kotlin promueve el uso de propiedades en lugar de simples campos, lo que permite definir lógica de acceso segura, validaciones y encapsulamiento.

También descubriste que val no significa que el contenido sea inmutable —solo que la referencia no puede cambiar—, y que las propiedades pueden tener getters y setters personalizados, lo cual es fundamental al diseñar bibliotecas robustas y seguras.

🔑 Puntos clave

  • val declara referencias inmutables, ideales para expresar valores que no cambian.
  • var permite referencias mutables, útiles para modelar cambios de estado.
  • Kotlin trata las variables como propiedades, no como campos directos, lo que habilita control total mediante getters y setters.
  • Es posible encapsular estructuras mutables internas usando campos de respaldo (private val _campo) y exponer vistas inmutables.
  • Los setters personalizados permiten validar y controlar el acceso de forma declarativa y segura.
  • Al diseñar bibliotecas, es clave exponer lo mínimo necesario y proteger las invariantes internas.

🧰 ¿Qué nos llevamos?

El diseño cuidadoso de variables y propiedades marca la diferencia entre una biblioteca frágil y una robusta. En Kotlin, las herramientas como val, var, getters y setters personalizados, y campos de respaldo te permiten controlar el acceso a la información, proteger el estado interno y hacer que tu API sea segura e intuitiva para otras personas.

Adoptar la inmutabilidad por defecto con val no solo mejora la legibilidad, sino que también facilita el mantenimiento y reduce errores sutiles. En cambio, usar var debe ser una decisión deliberada, motivada por una necesidad real de cambio de estado.

En resumen: escribe tus variables pensando en cómo serán usadas, qué deben permitir y qué deben evitar. Esa es la base de un buen diseño de bibliotecas.

📖 ¿Con ganas de más?

🔥 Referencias recomendadas

  • 🌐 Properties en la documentación oficial de Kotlin:
    Explica cómo declarar y usar propiedades (val y var) en Kotlin, incluyendo su sintaxis completa con inicializadores, getters y setters personalizados. Describe cómo controlar la visibilidad de los accesores (private set), cómo usar campos de respaldo (field) para almacenar valores internamente y cómo crear propiedades computadas sin necesidad de almacenamiento. También aborda propiedades con inicialización diferida (lateinit), constantes en tiempo de compilación (const val) y patrones avanzados mediante propiedades delegadas. Ideal para entender cómo Kotlin permite encapsular, validar y exponer estado de forma segura y expresiva.