ProgrammationDéveloppeur Kotlin

Qu'est-ce que les fonctions standard de niveau supérieur apply, also, let et run en Kotlin, comment diffèrent-elles les unes des autres et à quelles fins sont-elles utilisées ?

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

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.

Historique de la question

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.

Problème

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

Solution

L'utilisation des fonctions apply, also, let, run augmente l'expressivité :

val user = User().apply { name = "Alex" age = 26 isActive = true }

Descriptions brèves :

  • apply : renvoie l'objet ayant appelé (this), utilisé pour la configuration.
  • also : pour les effets secondaires, renvoie l'objet, le paramètre dans la lambda est it.
  • let : pour transformer une valeur (par exemple, pour des types nullable), renvoie le résultat de la lambda (valeur finale).
  • run : combine les capacités d'apply et let. Fonctionne avec this à l'intérieur de la lambda et renvoie le résultat de la lambda.

Exemple de code :

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 :

  • apply, also, let et run sont des outils importants pour un travail concis et expressif avec les objets.
  • Contexte à l'intérieur de la lambda : apply/run — this, let/also — it.
  • Leur utilisation correcte simplifie le code et réduit le risque d'erreurs lors de la configuration ou de la vérification des objets.

Questions piégées.

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 renvoie l'objet lui-même
  • run renvoie le résultat de l'exécution de la lambda
// 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}" }

Erreurs typiques et anti-modèles

  • Contextes confus (this/it), menant à une logique inattendue.
  • Utilisation de apply pour des opérations sans modification de l'objet.
  • Imbrication excessive et mélange de fonctions, diminuant la lisibilité.

Exemple de la vie réelle

Cas négatif

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 :

  • Compacité du code

Inconvénients :

  • Il est facile de se tromper sur la valeur renvoyée et de provoquer des effets secondaires inattendus
  • Difficile de maintenir le code et de trouver des bugs

Cas positif

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 :

  • Facile à lire et à maintenir
  • Correspondance claire avec l'objectif des fonctions

Inconvénients :

  • Nécessite de connaître les subtilités du fonctionnement de chaque fonction de portée