Historia de la cuestión:
En Java, la inmutabilidad de las colecciones se logra a través de envolturas como Collections.unmodifiableList() y el diseño de clases inmutables manualmente (campos final, ausencia de setters). En la práctica del desarrollo de Android y en el lado del servidor, esto se volvía costoso e incómodo, y la seguridad de los hilos y la previsibilidad de los datos se perdían.
Problema:
Las colecciones de Java no son seguras por defecto; siempre se puede obtener una referencia y modificar el objeto. Esto complica el control sobre la mutabilidad, especialmente en entornos multihilo, y conduce a errores difíciles de detectar.
Solución:
Kotlin se diseñó desde el principio con una separación entre colecciones de solo lectura y mutables, y la creación de objetos realmente inmutables se volvió más fácil gracias a val, data class y List/Set/Map sin el prefijo mutable. Además, se utiliza la copia a través de los métodos copy().
Ejemplo de código:
val nums: List<Int> = listOf(1, 2, 3) // lista inmutable // nums.add(4) // error de compilación val user = User(name = "Alex", age = 30) val updated = user.copy(age = 31) // Ejemplo con un mapa inmutable val state: Map<String, Int> = mapOf("a" to 1, "b" to 2)
Características clave:
¿Garantiza val la inmutabilidad absoluta del objeto contenido?
¡No! val garantiza solo que la variable no puede apuntar a otro objeto, pero se pueden cambiar los campos del objeto (si las propiedades internas son var).
¿Se puede obtener un objeto mutable de una colección de solo lectura?
Sí — si la colección contiene un objeto mutable, se puede cambiar su estado a pesar de la interfaz de solo lectura de la colección.
data class User(var name: String) val users: List<User> = listOf(User("Vasya")) users[0].name = "Petya" // permitido
¿Qué sucederá al convertir List<Int> a MutableList<Int> a través de as?
Se compilará, pero lanzará una ClassCastException en tiempo de ejecución si el tipo original era una interfaz de solo lectura.
Una aplicación de Android almacena el estado en val List<User>, creyendo que es inmutable. Uno de los desarrolladores modifica user.name. En otro fragmento, la lista actualizada muestra inesperadamente datos inconsistentes: el error aparece en la versión final.
Ventajas:
Desventajas:
Uso de data class inmutable solo con campos val, filtrando el acceso a objetos internos, actualización del estado a través de copy().
Ventajas:
Desventajas: