Propiedades Delegadas En Kotlin

En este tutorial te mostraremos que son las propiedades delegadas en Kotlin, con el propósito de redireccionar la lógica de los accesores y mutadores de una propiedad hacia un objeto auxiliar.

Delegación De Propiedades

Al igual que viste en el tutorial sobre delegación de clases, las propiedades también pueden encomendar la lógica de sus getters y setters a una expresión personalizada.

Aplicar el patrón de delegación sobre propiedades es de utilidad cuando la asignación y/o lectura requieren una lógica que deseas evitar realizar una y otra vez.

Aunque puedes crear la delegación manualmente, recuerda que Kotlin te provee la palabra reservada by para que el compilador genere todo el código repetitivo de la delegación. La sintaxis para ello es la siguiente:

// Sintaxis
val/var <nombrePropiedad>: <Tipo> by <expresion>

La expresión luego de by será la que produzca el objeto delegado. El cuál atenderá la llamada de get() y set() con sus propios métodos equivalentes.

Propiedades De Solo Lectura Con Delegado

Cuando desees crear un delegado para una propiedad de solo lectura, es necesario declararle un método operador llamado getValue(). Este será el que ejecutará la lógica del get() de la propiedad.

El resultado de getValue() debe ser del tipo o supertipo de la propiedad. Y los parámetros de su firma los configuras así:

  • thisRef: Es la referencia al dueño de la referencia
  • property: Es la referencia de la propiedad con tipo KProperty

Ejemplo: Crear una clase para líneas de factura con las propiedades de nombre de ítem, descripción, precio y cantidad comprada. Donde se debe delegar a la descripción la asignación de un String con el formato "Nombre de ítem. precio x cantidad = total".

import kotlin.reflect.KProperty

class InvoiceLine(val item: String, val price: Double, val quantity: Int) {
    val description: String by Description()
}

fun main() {
    val invoiceLine = InvoiceLine("Sandwich", 5.0, 1)
    println(invoiceLine.description)
}

class Description {
    operator fun getValue(thisRef: InvoiceLine, property: KProperty<*>): String {
        val total = thisRef.price * thisRef.quantity
        return buildString {
            append(thisRef.item)
            append("\n")
            append("$${thisRef.price}")
            append(" x ")
            append("${thisRef.quantity}")
            append(" = ")
            append("$$total")
        }
    }
}

Salida:

Sandwich
$5.0 x 1 = $5.0

La delegación parte con la creación de la clase Description y su método getValue(). Este usa a thisReft para acceder a la línea de la factura e invocar a las tres propiedades que serán usadas en la construcción del resultado final con la función buildString().

Luego consultamos a description e imprimimos su resultado, delegando así su producción a Description.

Propiedades Mutables Con Delegación

Similar a la delegación de propiedades de solo lectura, las propiedades mutables requieren la declaración del método setValue() en su delegado para redirigir la lógica de set().

Este método recibe un tercer parámetro que representa el nuevo valor asignado a la propiedad.

Ejemplo: Crear una clase lectora de enteros desde el teclado. Realizar la conversión de la entrada del usuario a Int a través de un delegado.

import kotlin.reflect.KProperty

class IntegerReader {
    var input: String? by UserInput()
    var number: Int = 0
}

class UserInput {
    operator fun getValue(ir: IntegerReader, property: KProperty<*>): String = ir.number.toString()

    operator fun setValue(ir: IntegerReader, property: KProperty<*>, value: String?) {
        ir.number = value?.toIntOrNull() ?: 0
    }
}

fun main() {
    val reader = IntegerReader()

    print("Digita número:")
    reader.input = readLine()

    println("Tu número es:${reader.number}")
}

Salida:

Digita número:125
Tu número es:125

En el código anterior creamos la clase IntegerReader() con dos propiedades: number para recibir el resultado de convertir a input.

La clase UserInput() representa al delgado de input. En getValue() obtenemos la conversión a String de number y en getValue() tomamos el parámetro value y lo convertimos a entero cada que sea modificado input.

¿Ha sido útil esta publicación?