Sintaxis avanzada de Kotlin: funciones de alcance y escenarios de aplicación

La biblioteca estándar de Kotlin proporciona varias funciones: let, run, with, apply y también, cuyo único propósito es ejecutar un bloque de código en el contexto de un objeto. Cuando se llama a una función de este tipo en un objeto y se proporciona una expresión lambda, se forma un ámbito temporal en el que se puede acceder al objeto sin su nombre. Estas funciones se denominan funciones de alcance.
Lo que estas funciones
tienen en común es que ejecutan un bloque de código en un objeto.
La diferencia: cómo se usa este objeto en el bloque de código y cuál es el resultado de la expresión completa.

ejecutar y aplicar funciones

función de ejecución: el objeto de contexto es el receptor de la expresión lambda, ejecuta una expresión lambda y devuelve el resultado de la expresión lambda

public inline fun <T, R> T.run(block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block()
}

función de aplicación: el objeto de contexto es el receptor de la expresión lambda, ejecuta una expresión lambda y devuelve el objeto de contexto

public inline fun <T> T.apply(block: T.() -> Unit): T {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block()
    return this
}

Lo que las funciones ejecutar y aplicar tienen en común es que el objeto de contexto sirve como receptor de la expresión lambda, pero los resultados devueltos son diferentes: la función ejecutar devuelve el resultado de la expresión lambda y la función aplicar devuelve el objeto de contexto en sí. .
Escenarios de uso:
tanto las funciones de ejecución como las de aplicación pueden entenderse como la asignación de un valor a un objeto o la realización de ciertas operaciones, y ambas pueden usarse, y a menudo se usan, para la inicialización de objetos.
El valor de retorno de la función de ejecución es el resultado de la expresión lambda, por lo que a menudo se usa en escenarios donde se requiere un valor de retorno después de realizar algunas operaciones (de inicialización).
El valor de retorno de la función de aplicación es el objeto de contexto en sí, por lo que se puede usar en llamadas en cadena para realizar algunas operaciones en objetos en un determinado enlace de la cadena de llamadas.

Ejemplo 1: La siguiente es una operación de inicialización. No hay requisitos para el valor de retorno. Podemos usar la función ejecutar o aplicar. No hay diferencia.

binding.recyclerView.run {
    
    
  layoutManager = LinearLayoutManager(this)
  addOnItemTouchListener(RecyclerView.SimpleOnItemTouchListener())
}
或者
binding.recyclerView.apply{
    
    
  layoutManager = LinearLayoutManager(this)
  addOnItemTouchListener(RecyclerView.SimpleOnItemTouchListener())
}

Ejemplo 2: de la siguiente manera, necesitamos realizar algunas operaciones en una persona, aumentar la edad en 10 años y finalmente devolver la edad aumentada en 10 años a una constante. No podemos usar aplicar aquí, solo ejecutar, porque no necesitamos la Persona en sí, pero el aumento de edad, que es el resultado de la expresión lambda

data class Person(var name: String, var sex: String, var age: Int) {
    
    
  override fun toString(): String {
    
    
    return "姓名:$name , 性别:$sex , 年龄:$age"
  }
}

val user = Person("小慧", "女", 18)
val ageOlder = user.run {
    
    
  age += 10
  Log.e("时光流逝", "$name 的年龄增加了10岁")
  age
}

Ejemplo 3: de la siguiente manera, hacemos llamadas en cadena, modificamos el objeto Persona a la mitad y finalmente imprimimos el objeto Persona modificado. En este momento, necesitamos usar aplicar en lugar de ejecutar, porque lo que necesitamos es el objeto Persona en sí, no el expresión lambda El resultado de la fórmula

data class Person(var name: String, var sex: String, var age: Int) {
    
    
  override fun toString(): String {
    
    
    return "姓名:$name , 性别:$sex , 年龄:$age"
  }
}
fun String.logE(tag: String) {
    
    
  Log.e(tag, this)
}

val user = Person("小慧", "女", 18)
user.apply {
    
    
name = "小米"
sex = "男"
age = 20
}.toString().logE("人物信息")

con función

con función: especifica el receptor de la expresión lambda, ejecuta una expresión lambda y devuelve el resultado de la expresión lambda, muy similar a la función ejecutar.

public inline fun <T, R> with(receiver: T, block: T.() -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return receiver.block()
}

Escenario de uso: Obtener un resultado después de realizar ciertas operaciones en un objeto. La semántica es más fuerte. El escenario de uso es introducir un objeto auxiliar cuyas propiedades o funciones se utilizarán para calcular un valor.
Caso: el código anterior se puede modificar en la función with, utilizando el usuario para calcular ageOlder.

val ageOlder = with(user) {
    
    
  age += 10
  Log.e("时光流逝", "$name 的年龄增加了10岁")
  age
}

dejar funcionar y también funcionar

función let: el objeto de contexto es el parámetro de la expresión lambda, ejecuta una expresión lambda y devuelve el resultado de la expresión lambda

public inline fun <T, R> T.let(block: (T) -> R): R {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    return block(this)
}

también funciona: el objeto de contexto es el parámetro de la expresión lambda, ejecuta una expresión lambda y devuelve el objeto de contexto en sí

public inline fun <T> T.also(block: (T) -> Unit): T {
    
    
    contract {
    
    
        callsInPlace(block, InvocationKind.EXACTLY_ONCE)
    }
    block(this)
    return this
}

Lo que las funciones let y also tienen en común es que el objeto de contexto se usa como parámetro de la expresión lambda, pero los resultados de retorno son diferentes. La función let devuelve el resultado de la expresión lambda y la función also devuelve el contexto. objeto en sí.

Escenarios de uso de la función let:
1. Llame a una o más funciones según el resultado de la cadena de llamadas:

ejemplo oficial:

val numbers = mutableListOf("one", "two", "three", "four", "five")
val resultList = numbers.map {
    
     it.length }.filter {
    
     it > 3 }
println(resultList)
替换为:
val numbers = mutableListOf("one", "two", "three", "four", "five")
numbers.map {
    
     it.length }.filter {
    
     it > 3 }.let {
    
     
    println(it)
    // 如果需要可以调用更多函数
} 

2.let se usa a menudo para ejecutar un bloque de código usando solo valores no nulos. Para realizar operaciones en un objeto no nulo, utilice el operador de llamada segura?. en él y llame a let para realizar la operación en una expresión lambda.

Ejemplo oficial:

val str: String? = "Hello" 
//processNonNullString(str)       // 编译错误:str 可能为空
val length = str?.let {
    
     
    println("let() called on $it")
    processNonNullString(it)      // 编译通过:'it' 在 '?.let { }' 中必不为空
    it.length
}

3. Otra situación en la que se utiliza let es introducir variables locales con alcance limitado para mejorar la legibilidad del código. Para definir una nueva variable para el objeto de contexto, proporcione su nombre como parámetro de expresión lambda en lugar del valor predeterminado.

Ejemplo oficial:

val numbers = listOf("one", "two", "three", "four")
val modifiedFirstItem = numbers.first().let {
    
     firstItem ->
    println("The first item of the list is '$firstItem'")
    if (firstItem.length >= 5) firstItem else "!" + firstItem + "!"
}.toUpperCase()
println("First item after modifications: '$modifiedFirstItem'")

Escenarios de uso de also:
also es útil para realizar algunas operaciones que toman un objeto de contexto como parámetro. Úselo también cuando necesite hacer referencia a un objeto en lugar de a sus propiedades y funciones, o cuando no desee bloquear estas referencias desde el alcance externo.

Ejemplo oficial:

val numbers = mutableListOf("one", "two", "three")
numbers
    .also {
    
     println("The list elements before adding new one: $it") }
    .add("four")

Resumen: El uso de let, run, with, apply y también es muy similar. En muchos casos, se pueden usar indistintamente. Las especificaciones de uso específicas pueden basarse en la comprensión personal y las especificaciones de desarrollo de la empresa.
Por ejemplo:
ejecutar y aplicar se usan generalmente para operaciones de inicialización (más inclinadas a operaciones de asignación). Debido a que aplicar devuelve el objeto de contexto en sí, aplicar también se usa a menudo en llamadas en cadena, en las que se asigna y modifica un objeto en la cadena. Espere para operaciones.
with se usa generalmente para asignar el resultado del cálculo y procesamiento basado en un objeto a otro objeto. Es muy similar a let. Hay
tres usos en documentos oficiales: 1.) Llamar a una o más funciones sobre el resultado de la cadena de llamadas. 2.) Cooperación ¿Llamada segura al operador?. Realizar operaciones no nulas 3.) Introducir variables locales con alcance limitado para mejorar la legibilidad del código.
También se utiliza para operaciones que requieren referenciar objetos en lugar de sus propiedades y funciones. Es muy similar a aplicar y Diferencia difícil: el objeto de contexto de apply es el receptor de la expresión lambda, y el objeto de contexto de también es el parámetro de la expresión lambda; y apply se usa principalmente para el cálculo de asignación de variables del objeto de contexto, mientras que también se usa para la operación del objeto de contexto en sí. De hecho, el límite entre los dos puede estar completamente determinado por la propia comprensión o normas de cada uno.

funciones takeIf y takeUnless

función tomarif:

public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
    
    
    contract {
    
    
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (predicate(this)) this else null
}

tomar a menos que la función

public inline fun <T> T.takeUnless(predicate: (T) -> Boolean): T? {
    
    
    contract {
    
    
        callsInPlace(predicate, InvocationKind.EXACTLY_ONCE)
    }
    return if (!predicate(this)) this else null
}

Función takeIf: el objeto de contexto es el parámetro de la expresión lambda. La expresión lambda devuelve un valor booleano. Si es verdadero, devuelve el objeto de contexto en sí, de lo contrario devuelve nulo. Es decir, se ejecuta una parte de la lógica en el objeto de contexto. Si la lógica es verdadera, se devuelve el objeto de contexto. Si la lógica es falsa, se devuelve nulo. La función takeUnless tiene la lógica opuesta a la función takeIf. No es necesario pensar en escenarios de aplicación: los datos se filtran según las condiciones.

Ejemplo oficial:

val number = Random.nextInt(100)

val evenOrNull = number.takeIf {
    
     it % 2 == 0 }
val oddOrNull = number.takeUnless {
    
     it % 2 == 0 }

Al encadenar llamadas a otras funciones después de takeIf y takeUnless, no olvide realizar comprobaciones nulas o llamadas seguras (?.), porque sus valores de retorno son anulables.

Supongo que te gusta

Origin blog.csdn.net/weixin_43864176/article/details/128483725
Recomendado
Clasificación