ProgrammationDéveloppeur Kotlin Moyen

Qu'est-ce que les fonctions de portée en Kotlin (let, also, run, apply, with)? Quelles sont les différences entre ces fonctions, comment les choisir pour différentes tâches, quelles subtilités peuvent survenir lors de leur utilisation? Donnez des exemples.

Réussissez les entretiens avec l'assistant IA Hintsage

Réponse.

Les fonctions de portée ("fonctions de domaine") en Kotlin sont des fonctions standard (let, also, run, apply, with) qui permettent de gérer le contexte d'exécution d'un bloc de code pour un objet. Elles se distinguent par :

  • le type de valeur de retour,
  • la manière dont l'objet est accessible à l'intérieur du bloc : via it ou via this.

Comparaison rapide :

Fonctionthis/itRetournePour quoi faire ?
letitrésultatchaîne d'opérations, travail avec nullable, map
alsoitobjeteffets secondaires, journalisation, débogage
runthisrésultatcalculs, initialisation avec retour
applythisobjetconfiguration d'objet, constructeurs
withthisrésultattravail avec API externes, objet "dehors"

Exemples :

  • let : pratique si l'objet est nullable :
val str: String? = "Texte" str?.let { println(it.length) }
  • apply : configuration de l'objet :
val paint = Paint().apply { color = Color.RED strokeWidth = 2f }
  • run : exécution sur un objet, retourne le résultat :
val length = "abcde".run { length }
  • with : pour travailler avec un objet externe :
val sb = StringBuilder() with(sb) { append("Bonjour, ") append("monde!") toString() }
  • also : pour les effets secondaires (par exemple, les logs) :
val list = mutableListOf(1, 2, 3) list.also { println("Avant: $it") }.add(4)

Points à noter :

  • let crée une copie de l'objet dans it, modifier les propriétés de l'objet n'est pas très pratique.
  • apply et also retournent toujours l'objet lui-même (this / it), utile pour les constructeurs.
  • run/with sont souvent confondus : with est une fonction ordinaire, pas une extension.

Question piège.

Quelle est la différence entre let et also ?

Réponse:

  • Tous deux utilisent it à l'intérieur du bloc,
  • let retourne le résultat de la lambda, souvent utilisé pour les chaînes de transformation,
  • also retourne l'objet initial, utilisé pour les effets secondaires (log, débogage), afin de ne pas interférer dans la chaîne de transformation.

Exemple :

val result = listOf(1).also { println(it) }.map { it * 2 } // résultat — List<Int>

Exemples d'erreurs réelles dues à l'ignorance des subtilités du sujet :


Histoire

Un débutant a utilisé let pour configurer un objet, pensant qu'il pouvait ainsi changer son état "dans la chaîne". En conséquence, à la fin du bloc de configuration, il obtenait non pas l'objet, mais le résultat de la lambda (par exemple, rien), ce qui rompait la chaîne de création DSL.


Histoire

En écrivant du code pour travailler avec des objets nullable, run a été utilisé au lieu de let, ne remarquant pas la différence dans la valeur de retour. Au final, le résultat de l'expression était différent de l'attendu, des null apparaissaient là où ils ne devraient pas — la logique de l'application était rompue.


Histoire

Dans un grand constructeur, with a été accidentellement utilisé pour des objets internes, en comptant sur le modèle d'extension. Puisque with n'est pas une fonction d'extension, la chaîne de plusieurs blocs with ne fonctionnait pas correctement, les appels internes étaient confus et sortaient des limites de l'objet actuel. Il a fallu réécrire complètement la hiérarchie de création des objets.