Dans le langage Kotlin, les fonctions standard de niveau supérieur apply, also, let et run ont été introduites pour simplifier la configuration en chaîne des objets et les modifications locales avec un minimum de code répétitif. Ces fonctions facilitent le travail avec des objets mutables et immuables, permettent d'exprimer de manière concise des chaînes de transformations, et résolvent en partie des problèmes liés aux portées temporelles des variables.
Ces fonctions ont été empruntées au modèle builder et aux approches des interfaces fluides. Leur apparition est due à la volonté de rendre le code plus propre et de le débarrasser de la déclaration excessive de variables auxiliaires.
L'approche classique nécessite plusieurs appels à l'objet ou la sortie de variables temporaires. Cela réduit la lisibilité et augmente le risque d'erreurs :
val user = User() user.name = "Alex" user.age = 26 user.isActive = true
L'utilisation des fonctions apply, also, let, run augmente l'expressivité :
val user = User().apply { name = "Alex" age = 26 isActive = true }
Descriptions brèves :
data class User(var name: String = "", var age: Int = 0, var isActive: Boolean = false) val configuredUser = User().apply { name = "Alice" age = 30 isActive = true } debugUser(configuredUser.also { println("User est configuré : $it") }) val emailLength = configuredUser.email?.let { it.length } ?: 0 val description = configuredUser.run { "$name ($age)" }
Caractéristiques clés :
La fonction let peut-elle modifier l'objet avec lequel elle travaille ?
La fonction let n'est pas destinée à modifier l'objet. Elle sert plutôt à appliquer une transformation à une valeur et renvoie le résultat. Il convient de rappeler qu'à l'intérieur de let, c'est it qui est disponible au lieu de this.
val upperName = user.name.let { it.uppercase() } // let ne modifie pas user
Quelle est la différence entre apply et run ?
Les deux fonctionnent avec le contexte this à l'intérieur de la lambda, la différence réside dans la valeur retournée :
// apply val building = StringBuilder().apply { append("start-") append("end") } // building est un StringBuilder // run val result = StringBuilder().run { append("start-") append("end") toString() } // result est un String
Peut-on imbriquer l'application de apply et let ? Si oui, quand cela est-il justifié ?
Oui, l'imbrication est doucement recommandée pour l'agrégation ou la configuration pas à pas des objets, notamment lors du travail avec des nullable :
val userInfo = user?.apply { isActive = true }?.let { "${it.name} est actif : ${it.isActive}" }
L'utilisation de let dans tout le code pour modifier un objet, mélangeant apply et let — au final, l'objet ne change pas là où cela était attendu, et sa valeur se perd dans les chaînes.
Avantages :
Inconvénients :
Utilisation de apply et also pour la configuration et la journalisation, let uniquement pour travailler avec des nullable et obtenir un résultat, run — pour les chaînes de transformations nécessitant le calcul d'une nouvelle valeur.
Avantages :
Inconvénients :