Syntaxe Kotlin avancée – fonctions de portée et scénarios d'application

La bibliothèque standard Kotlin propose plusieurs fonctions : let, run, with, apply et aussi, dont le seul but est d'exécuter un bloc de code dans le contexte d'un objet. Lorsqu'une telle fonction est appelée sur un objet et qu'une expression lambda est fournie, elle forme une portée temporaire dans laquelle l'objet est accessible sans son nom. Ces fonctions sont appelées fonctions de portée.
Le point commun de ces fonctions
est qu’elles exécutent un bloc de code sur un objet.
La différence : comment cet objet est utilisé dans le bloc de code et ce que renvoie l'expression entière.

exécuter et appliquer des fonctions

fonction run : l'objet contextuel est le récepteur de l'expression lambda, exécute une expression lambda et renvoie le résultat de l'expression lambda

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

fonction apply : l'objet contextuel est le récepteur de l'expression lambda, exécute une expression lambda et renvoie l'objet contextuel

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

Ce que les fonctions run et apply ont en commun, c'est que l'objet contextuel sert de récepteur à l'expression lambda, mais les résultats renvoyés sont différents. La fonction run renvoie le résultat de l'expression lambda et la fonction apply renvoie l'objet contextuel lui-même. .
Scénarios d'utilisation :
les fonctions d'exécution et d'application peuvent être comprises comme l'attribution d'une valeur à un objet ou l'exécution de certaines opérations, et les deux peuvent et sont souvent utilisées pour l'initialisation d'un objet.
La valeur de retour de la fonction run est le résultat de l'expression lambda, elle est donc souvent utilisée dans les scénarios où une valeur de retour est requise après avoir effectué certaines opérations (d'initialisation).
La valeur de retour de la fonction apply est l'objet contextuel lui-même, elle peut donc être utilisée dans les appels en chaîne pour effectuer certaines opérations sur les objets dans un certain lien de la chaîne d'appel.

Exemple 1 : ce qui suit est une opération d'initialisation. Il n'y a aucune exigence concernant la valeur de retour. Nous pouvons utiliser la fonction run ou apply. Il n'y a aucune différence.

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

Exemple 2 : Comme suit, nous devons effectuer certaines opérations sur une personne, augmenter l'âge de 10 ans et enfin ramener l'âge augmenté de 10 ans à une constante. Nous ne pouvons pas utiliser apply ici, seulement exécuter, car nous n'avons pas besoin la Personne elle-même, mais l'augmentation de l'âge, qui est le résultat de l'expression 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
}

Exemple 3 : comme suit, nous effectuons des appels en chaîne, modifions l'objet Person à mi-chemin et enfin imprimons l'objet Person modifié. À ce stade, nous devons utiliser apply au lieu de run, car ce dont nous avons besoin, c'est de l'objet Person lui-même, pas du expression lambda. Le résultat de la formule

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("人物信息")

avec fonction

with function : spécifie le récepteur de l'expression lambda, exécute une expression lambda et renvoie le résultat de l'expression lambda, très similaire à la fonction run.

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

Scénario d'utilisation : Obtenir un résultat après avoir effectué certaines opérations sur un objet. La sémantique est plus forte. Le scénario d'utilisation consiste à introduire un objet auxiliaire dont les propriétés ou fonctions seront utilisées pour calculer une valeur.
Cas : Le code ci-dessus peut être modifié dans la fonction with, en utilisant l'utilisateur pour calculer ageOlder.

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

laisser fonctionner et aussi fonctionner

fonction let : l'objet contexte est le paramètre de l'expression lambda, exécute une expression lambda et renvoie le résultat de l'expression lambda

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

fonctionne également : l'objet contextuel est le paramètre de l'expression lambda, exécute une expression lambda et renvoie l'objet contextuel lui-même

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

Ce que les fonctions let et aussi ont en commun, c'est que l'objet de contexte est utilisé comme paramètre de l'expression lambda, mais les résultats de retour sont différents. La fonction let renvoie le résultat de l'expression lambda, et la fonction aussi renvoie le contexte objet lui-même.

Scénarios d'utilisation de la fonction let :
1. Appeler une ou plusieurs fonctions sur le résultat de la chaîne d'appel :

exemple officiel :

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 est souvent utilisé pour exécuter un bloc de code en utilisant uniquement des valeurs non nulles. Pour effectuer des opérations sur un objet non nul, utilisez l'opérateur d'appel sécurisé ?. dessus et appelez let pour effectuer l'opération dans une expression lambda.

Exemple officiel :

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

3. Une autre situation dans laquelle let est utilisé consiste à introduire des variables locales de portée limitée pour améliorer la lisibilité du code. Pour définir une nouvelle variable pour l'objet contextuel, fournissez son nom en tant que paramètre d'expression lambda au lieu de celui par défaut.

Exemple officiel :

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'")

Scénarios d'utilisation de also :
 also est utile pour effectuer certaines opérations qui prennent un objet contextuel comme paramètre. À utiliser également lorsque vous devez faire référence à un objet plutôt qu'à ses propriétés et fonctions, ou lorsque vous ne souhaitez pas bloquer ces références de la portée externe.

Exemple officiel :

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

Résumé : Les utilisations de let, run, with, apply et sont également très similaires. Dans de nombreux cas, ils peuvent être utilisés de manière interchangeable. Les spécifications d'utilisation spécifiques peuvent être basées sur la compréhension personnelle et les spécifications de développement de l'entreprise.
Par exemple :
 run et apply sont généralement utilisés pour les opérations d'initialisation (plus enclins aux opérations d'affectation). Comme apply renvoie l'objet contextuel lui-même, apply est également souvent utilisé dans les appels en chaîne, dans lesquels un objet de la chaîne est attribué et modifié. Attendez pour les opérations.
with est généralement utilisé pour attribuer le résultat d'un calcul et d'un traitement basé sur un objet à un autre objet. Il est très similaire à let. Il existe
trois usages dans les documents officiels : 1.) Appeler une ou plusieurs fonctions sur le résultat de la chaîne d'appel 2.) Coopérer Opérateur d'appel sécurisé ?. Effectuer des opérations non nulles 3.) Introduire des variables locales avec une portée limitée pour améliorer la lisibilité du code.
Également utilisé pour les opérations qui nécessitent de référencer des objets au lieu de leurs propriétés et fonctions. Il est très similaire à appliquer et Distinction difficile : l'objet contextuel de apply est le récepteur de l'expression lambda, et l'objet contextuel de est également le paramètre de l'expression lambda ; et apply est principalement utilisé pour le calcul d'affectation des variables de l'objet contextuel, tout en étant également utilisé pour le fonctionnement de l’objet contextuel lui-même. En fait, la frontière entre les deux peut être entièrement déterminée par sa propre compréhension ou ses propres normes.

Fonctions takeIf et takeUnless

fonction takeif :

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

fonction takeUnless

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

Fonction takeIf : L'objet contextuel est le paramètre de l'expression lambda. L'expression lambda renvoie une valeur booléenne. Si elle est vraie, elle renvoie l'objet contextuel lui-même, sinon elle renvoie null. C'est-à-dire qu'un élément de logique est exécuté sur l'objet contextuel. Si la logique est vraie, l'objet contextuel est renvoyé. Si la logique est fausse, null est renvoyé. La fonction takeUnless a la logique opposée à la fonction takeIf. Pas besoin de penser à des scénarios d’application : les données sont filtrées selon des conditions.

Exemple officiel :

val number = Random.nextInt(100)

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

Lorsque vous enchaînez les appels à d'autres fonctions après takeIf et takeUnless, n'oubliez pas d'effectuer des vérifications nulles ou des appels sécurisés (?.), car leurs valeurs de retour sont nullables.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_43864176/article/details/128483725
conseillé
Classement