Funciones estándar (Scope Functions) II: with y run

Funciones estándar (Scope Functions) II: run y with

Después de presentar las funciones estándar en Kotlin, ver las principales diferencias entre ellas y detenernos en la función let (ver Funciones estándar (Scope Functions) I: let), en esta segunda parte veremos otras dos funciones estándar: with y run.

with

En esta función el objeto se pasa como un argumento pero dentro de la lambda y está disponible como un receptor (this). Permite acceder a los miembros de su argumento de forma concisa al omitir el nombre de la instancia.

Aunque su valor de retorno es el resultado lambda, se suele recomendar su uso para llamar a funciones sin proporcionar su resultado. Por ejemplo, en el siguiente código with se puede interpretar como "con este objeto (numeros), hacer los siguiente":
fun main() {
    val numeros = mutableListOf("uno", "dos", "tres")
    with(numeros) {
        print("'with' es llamado con el argumento $this, ")
        println("que es un ${this::class.simpleName} con $size elementos.")
    }
}
// 'with' es llamado con el argumento [uno, dos, tres], que es un ArrayList con 3 elementos.

Otro uso común de with es la introducción de un objeto auxiliar cuyas propiedades o funciones se utilizarán para calcular un valor, por ejemplo:
fun main() {
    val numeros = mutableListOf(1, 3, 5, 7, 9)
    val inicioFin = with(numeros) {
        "El primer elemento es ${first()}, el último elemento es ${last()}"
    }
    println(inicioFin)
    // El primer elemento es 1, el último elemento es 9
}

Otro ejemplo:
class Configuracion(var host: String, var port: Int)

fun main() {
    val config = Configuracion(host = "127.0.0.1", port = 8080)
    with(config) {
        println("$host:$port") // 127.0.0.1:8080
    }
    // en lufar de:
    // println("${config.host}:${config.port}")
}

run

En la función run el objeto está disponible como un receptor (this) y su valor de retorno es el resultado de lambda.

run es útil cuando la lambda contiene tanto la inicialización del objeto como el cálculo del valor de retorno. Básicamente hace lo mismo que let: ejecuta un bloque de código y devuelve su resultado con la diferencia de que se accede al objeto con this, lo que resulta útil cuando interesa llamar a los métodos del objeto en lugar de pasarlo como argumento.
class MultiportService(var url: String, var port: Int) {
    fun prepareRequest(): String = "Default request"
    fun query(request: String): String = "Result for query '$request'"
}

fun main() {
    val service = MultiportService("https://kotlindoc.blogspot.com", 80)
    val result = service.run {
        port = 8080
        query(prepareRequest() + " to port $port")
    }
    println(result)
    // Result for query 'Default request to port 8080'
}

Y el mismo código con la función let sería:
val result = service.let {
    it.port = 8080
    it.query(it.prepareRequest() + " to port ${it.port}")
}

Además de llamar a run sobre un objeto receptor, podemos usarlo en una expresión para ejecutar un bloque de varias declaraciones:
fun main() {
    val hexNumberRegex = run {
        val digitos = "0-9"
        val letras = "A-Fa-f"
        val signo = "+-"
        Regex("[$signo]?[$digitos$letras]+")
    }

    print("Números hexadecimales válidos: ")
    for (match in hexNumberRegex.findAll("A41E1 -FFFF -x? 42ef")) {
        print("${match.value} ")
    }
}

Otro ejemplo de la función estándar run:
fun main() {
    fun longitudAnulable(ns: String?) {
        println("Calculando longitud de \"$ns\":")
        if (ns == null) println("\tArgumento nulo")
        ns?.run {
            // si no es null, ejecuta el bloque
            println("\t¿Vacío? " + isEmpty()) // dentro de run se accede a los miembros del objeto sin su nombre
            println("\tCaracteres: $length")
            length // run devuelve la longitud de la cadena (si no es nula)
        }
    }
    longitudAnulable(null)
    longitudAnulable("")
    longitudAnulable("Kotlin Doc")
}
Calculando longitud de "null":
    Argumento nulo
Calculando longitud de "":
    ¿Vacío? true
    Caracteres: 0
Calculando longitud de "Kotlin Doc":
    ¿Vacío? false
    Caracteres: 10

Comentarios

Entradas populares

Recursos gratis para aprender Kotlin

I/O: entrada y salida de datos en consola

Lectura y escritura de archivos