Kotlin const vs val: ¿cuál es la diferencia y qué elegir?
\n\n¿Estás escribiendo código en Kotlin y de repente te preguntas: «¿Cuál es la diferencia entre const y val? Ambos parecen inmutables, ¿verdad?». ¡Excelente pregunta! Este aspecto confunde incluso a desarrolladores experimentados que vienen de Java u otros lenguajes. No es de extrañar que en Stack Overflow el tema «What is the difference between "const" and "val"?» acumule miles de visitas. Hoy vamos a aclarar este asunto de una vez por todas, desglosando la teoría y respaldándola con ejemplos de código en vivo.
¿Listo? ¡Allá vamos!
\n\nBreve introducción: ¿qué es val?
\n\nEmpecemos por lo más simple. val (de la palabra value — valor) es una referencia inmutable. Puedes asignarle un valor una sola vez y nunca más cambiarlo. ¿Te suena familiar? Sí, es el equivalente de final en Java o readonly en C#.
val name: String = "Alicia"\nname = "Bob" // ¡Error de compilación! Val cannot be reassigned\n\nPero hay un matiz importante: val garantiza que la referencia no cambiará, pero no garantiza la inmutabilidad del objeto en sí. Si usas val con una lista mutable (MutableList), aún puedes agregar y eliminar elementos.
val numbers = mutableListOf(1, 2, 3)\nnumbers.add(4) // ¡Funciona!\n// numbers = mutableListOf(5, 6) // ¡Error! No se puede reasignar la referencia\n\nval puede inicializarse tanto en el momento de la declaración como más tarde en el código (por ejemplo, en el constructor o en un bloque init). Su valor se calcula durante la ejecución del programa (runtime).
¿Y qué es const? El estricto guardián del tiempo de compilación
\n\nconst es el hermano más estricto de val. También es inmutable, pero con una supercapacidad: el valor debe conocerse en tiempo de compilación (compile-time). En otras palabras, es una constante real que el compilador puede insertar directamente en el código, como una función incorporada.
const tiene reglas estrictas:
- \n
- Solo puede declararse a nivel de archivo (fuera de una clase) o dentro de un
object/companion object. \n - Debe ser de tipo primitivo o de tipo
String. \n - No puede ser el resultado de una llamada a función o un cálculo; solo un literal u otra constante. \n
// Correcto\nconst val PI = 3.14159\nconst val APP_NAME = "MiApp"\n\n// Incorrecto\nconst val currentTime = System.currentTimeMillis() // ¡Error! El valor no se conoce en tiempo de compilación\nconst val userName = getName() // ¡Error! No se permite llamada a función\n\nPrincipales diferencias: const vs val en tabla
\n\nPara que sea visual, aquí tienes una tabla comparativa:
\n\n| Criterio | \nconst val | \nval | \n
|---|---|---|
| Momento de inicialización | \nEn tiempo de compilación | \nEn tiempo de ejecución | \n
| Dónde se puede declarar | \nNivel superior, object, companion object | \nEn cualquier lugar (dentro de una clase, función, constructor) | \n
| Tipos de datos | \nSolo primitivos y String | \nCualquier tipo (incluyendo objetos, listas) | \n
| Rendimiento | \nMáximo (inline) | \nEstándar | \n
| Uso en anotaciones | \nSí | \nNo | \n
Ejemplo práctico: anotaciones y rendimiento
\n\nUno de los usos más