Propiedades Respaldadas Por Mapas

En este tutorial verás el uso de propiedades respaldadas por mapas. Esta es otra utilidad de las propiedades delegadas, donde usarás mapas como delegados para la lectura y escritura de los valores de tus propiedades.

Delegar A Mapa Inmutable

Para tomar el valor de las propiedades delegadas desde en un mapa, usa la cláusula by en la declaración de la misma, seguido del mapa que actuará como delegado.

val propiedad: Tipo by mapa

La propiedad tomará el valor del par al que corresponde su nombre como clave. Esta lógica se ejecuta dentro de la funciones de extensión Map<K, V>.getValue().

Ejemplo:

Crear el modelo de un jugador de NBA a partir de un mapa con sus datos básicos: primer nombre, apellido, ID de jugador e ID de equipo.

class NbaPlayer(nbaInfo: Map<String, Any?>) {
    val firstName: String by nbaInfo
    val lastName: String by nbaInfo
    val playerId: Int by nbaInfo
    val teamId: Int by nbaInfo

    override fun toString(): String {
        var toPrint = "Nombre: $firstName\n"
        toPrint += "Apellido: $lastName\n"
        toPrint += "Id jugador: $playerId\n"
        toPrint += "Id equipo: $teamId\n"
        return toPrint
    }
}

La solución anterior declara la clase NbaPlayer con el mapa nbaInfo como parámetro de entrada. Luego definimos 4 propiedades de solo lectura para los datos y se las delegamos al mapa.

Ahora creemos una instancia de NbaPlayer y deleguemos su inicialización a un mapa cuyas claves correspondan a las propiedades:

fun main() {
    val playerMap = mapOf(
        "firstName" to "Jaylen",
        "lastName" to "Adams",
        "playerId" to 1629121,
        "teamId" to 1610612749
    )
    val player1 = NbaPlayer(playerMap)
    
    println(player1)
}

Salida:

Nombre: Jaylen
Apellido: Adams
Id jugador: 1629121
Id equipo: 1610612749

Claro está que si el nombre de la clave no coincide con el de la propiedad, tendrás una excepción NoSuchElementException. Y si el tipo de los valores difiere, será una ClassCastException.

Delegar A Mapa Mutable

Claro está que las propiedades mutables deben delegar a instancias de MutableMap para cubrir la modificación de su valor. Esto permitirá dirigir el valor asignado a las propiedades hacia mapa delegado si así lo deseas.

La convención de la delegación es posible, porque MutableMap tiene definido el operador setValue() para confiarle la lógica de los setters en las propiedades delegadas.

Ejemplo:

A partir de un mapa mutable que contiene el perfil de un usuario, inicializar una variable que almacene el nick y lo muestre.

fun main() {
    val userProfile = loadUserProfile()

    val displayName by userProfile
    println("displayName es $displayName")

    userProfile["displayName"] = "ProtosWin"
    println("displayName es $displayName")
}

private fun loadUserProfile() = mutableMapOf(
    "displayName" to "Killer123",
    "avatar" to "avatar_22",
    "language" to "es"
)

Salida:

displayName es Killer123
displayName es ProtosWin

En este ejemplo tenemos la variable inmutable local displayName. Variable que delega su valor al mapa userProfile construido a partir de loadUserProfile().

Como puede observar, el valor inicial para la clave "displayName" es "Killer123". Este se entrega a la variable displayName sin problemas, ya que el nombre de la propiedad coincide con la clave.

Luego modificamos el valor por "ProtosWin" e imprimimos de nuevo el valor de displayName corroborando que su valor ha sido modificado, incluso si es val.

Por otro lado, si deseas una modificación bidireccional entre propiedad y mapa, entonces declara la propiedad como var. Al modificarla el mapa también modificará el valor del par asociado.

fun main() {
    val userProfile = loadUserProfile()

    // Propiedad var y mapa mutable
    println("userProfile[\"language\"] -> ${userProfile["language"]}")
    
    var language by userProfile
    language = "en"
    
    println("userProfile[\"language\"] -> ${userProfile["language"]}")
}

Salida:

userProfile["language"] -> es
userProfile["language"] -> en

Al asignar el valor "en" a language, el valor del par será mutado al nuevo estado como se ve en la impresión.

Únete Al Discord De Develou

Si tienes problemas con el código de este tutorial, preguntas, recomendaciones o solo deseas discutir sobre desarrollo Android conmigo y otros desarrolladores, únete a la comunidad de Discord de Develou y siéntete libre de participar como gustes. ¡Te espero!