En este tutorial verás cómo usar data classes en Kotlin para facilitar el uso de clases cuya responsabilidad es solo almacenar valores. Aprenderás sobre el modificador data
, la representación String, la copia de objetos, comparación de igualdad y desestructuración de data classes.
El Modificador data
El modificador data
facilita la creación de clases cuyo propósito es solo almacenar valores. A estas clases se les llama, clases de datos o data classes en Kotlin.
Por ejemplo, una clase de datos para almacenar datos de rocas generadas, que pertenecen a un terreno:
data class Rock(
val witdh: Int,
val heith: Int,
val depth: Int,
val variation: Int = 1
)
Al marcar la clase Rock
con data
, este le dice al compilador Kotlin que le autogenere los siguientes métodos, tomando como base sus propiedades en el constructor primario:
equals
hashCode
copy
toString
- N métodos
componentN()
que corresponden a cada propiedad en orden de declaración
Nota: Existen algunos requerimientos y reglas de herencia a la hora de crear clases de datos. Puedes leerlas en la guía oficial.
Estos método te utilidad proveen implementaciones que te facilitarán varias acciones sobre las data classes como verás a lo largo de este tutorial.
Excluir Propiedades
El compilador solo usa las propiedades que se encuentra en el constructor principal para autogenerar los métodos de utilidad.
Si deseas excluir alguna, entonces declarara al interior del cuerpo de la clase de datos.
data class Rock(
val witdh: Int,
val heith: Int,
val depth: Int,
val variation: Int = 1
){
var deformationFactor = 0.0
}
La declaración anterior evita que la propiedad deformationFactor
haga parte del cómputo.
Representación String
El método autogenerado toString()
retorna una representación del objeto con el estilo:
Clase(propiedad1=valor1, propiedad2=valor2, propiedadN=valorN)
Por ejemplo, si creas un ejemplar de Rock
y lo imprimes:
fun main() {
val rock1 = Rock(20, 10, 5)
println(rock1)
}
El resultado en pantalla será:
Rock(witdh=20, heith=10, depth=5, variation=1)
Copiar Una Instancia
Usa el método copy()
de una data class
para copiar los valores de un objeto en otro.
Por ejemplo, si creas dos instancias de la clase Rock
y usas el método copy()
de la primera instancia para inicializar la segunda.
fun main() {
val rock1 = Rock(20, 10, 5)
val rock2 = rock1.copy()
println("Roca 1:$rock1")
println("Roca 2:$rock2")
}
Al imprimir, sus contenidos tendrán los mismos valores:
Roca 1:Rock(witdh=20, heith=10, depth=5, variation=1)
Roca 2:Rock(witdh=20, heith=10, depth=5, variation=1)
También es posible especificar en el copiado, argumentos nombrados para usar valores diferentes en las propiedades.
val rock3 = rock1.copy(variation = 4)
Igualdad De Instancias
El método equals()
compara el contenido significativo de los objetos. Es decir, dos objetos serán iguales si los valores de sus propiedades son iguales.
Para simplificar el llamado de equals()
, puedes usar el operador de comparación ==
.
Por ejemplo, compara la igualdad de la roca 1 con la 2 y de la 2 con la 3:
fun main() {
val rock1 = Rock(20, 10, 5)
val rock2 = rock1.copy()
val rock3 = rock1.copy(variation = 4)
println("rock1 == rock2: ${rock1 == rock2}")
println("rock2 == rock3: ${rock2 == rock3}")
}
Al correr la aplicación se imprimirá:
rock1 == rock2: true
rock2 == rock3: false
Por otro lado, si quieres comprobar la igualdad de las referencias en memoria de dos objetos, usa el triple operador de igual ===:
println("rock1 === rock2: ${rock1 === rock2}")
Código Hash
El método autogenerado hashCode()
es usado a la par con equals()
, ya que dos instancias de una data class son iguales si su entero hash es igual.
Por ejemplo, al retornar los códigos hash de la roca 1 y 2:
println("rock1.hashCode():${rock1.hashCode()}")
println("rock2.hashCode():${rock2.hashCode()}")
Ya que ambas tienen la misma semántica de propiedades generadas por el modificador data
, los enteros impresos serán iguales:
rock1.hashCode():605586
rock2.hashCode():605586
Desestructurar Data Classes
Como habías en la sección inicial, a las data classes también se les otorgan n métodos escritos como componentN()
. También llamadas funciones de componentes.
El índice que reemplaza a N, es la posición de cada propiedad en la declaración.
Por ejemplo, puedes imprimir el valor de las 4 propiedades de rock1
así:
fun main() {
val rock1 = Rock(20, 10, 5)
println("witdh = ${rock1.component1()}")
println("height = ${rock1.component2()}")
println("depth = ${rock1.component3()}")
println("variation = ${rock1.component4()}")
}
La salida será:
witdh = 20
height = 10
depth = 5
variation = 1
La generación de las funciones de componentes, permiten desestructurar declaraciones de creación de instancias de las data classes en Kotlin.
Por ejemplo, desestructura la creación de una nueva roca en tan solo el ancho y su alto:
fun main() {
val (witdh, height) = Rock(15, 15, 15)
println("Desestructurando a width($witdh) y height($height)")
}
Internamente, el compilador de Kotlin llama a component1()
y component2()
para asignarlo a las variables en paréntesis. Y luego puedes usarlas individualmente.
¿Qué Sigue?
En este tutorial viste como usar data classes en Kotlin y los diferentes usos de sus métodos de utilidad. Ahora puedes ir al tutorial de clases enum.