Les fonctions de portée dans Kotlin sont cinq fonctions de la bibliothèque standard (let, run, with, apply, also) qui exécutent un bloc de code dans le contexte d'un objet. Elles diffèrent par la façon d'accéder à l'objet (via it ou this) et la valeur de retour (objet de contexte ou résultat de la lambda). Selon la documentation Kotlin (2026), le bon choix de fonction de portée réduit le code passe-partout de 25 à 40 %. Les fonctions de portée aident à écrire l'initialisation, la configuration et les transformations d'objets dans un style déclaratif.
Points clés
Les fonctions de portée sont un ensemble de cinq fonctions (let, run, with, apply, also) intégrées à la bibliothèque standard de Kotlin. Chacune d'elles prend une lambda et modifie temporairement la portée afin que le code à l'intérieur de la lambda s'exécute dans le contexte d'un objet spécifique. Cela évite de répéter le nom de l'objet pour plusieurs appels et regroupe les opérations connexes dans un seul bloc.
Deux caractéristiques clés distinguent les fonctions de portée : la façon d'accéder à l'objet de contexte (via this ou via it) et la valeur de retour (l'objet de contexte lui-même ou le résultat de la lambda). L'accès via this fait de l'objet un récepteur implicite — les méthodes et propriétés sont appelées sans préfixe. L'accès via it passe l'objet comme paramètre explicite de la lambda. Retourner l'objet permet le chaînage, retourner le résultat permet d'assigner la valeur transformée.
Historiquement, les fonctions de portée sont apparues comme un remplacement du modèle classique Builder et des variables temporaires pour l'initialisation. Au lieu de créer une classe builder séparée, le développeur écrit apply { propriété1 = ...; propriété2 = ... }. Cela réduit la quantité de code et rend l'initialisation plus lisible. Dans l'écosystème Kotlin, les fonctions de portée sont utilisées dans tous les frameworks et bibliothèques comme une idiome standard du langage.
Le choix de la bonne fonction de portée est déterminé par deux questions : souhaitez-vous utiliser this ou it pour accéder à l'objet, et souhaitez-vous retourner l'objet lui-même ou le résultat de la lambda ? La combinaison de deux caractéristiques binaires donne quatre options possibles, et with occupe une place spéciale en tant que seule fonction qui n'est pas une extension.
| Fonction | Accès | Retour | Extension ? | Scénario typique |
|---|---|---|---|---|
| let | it | résultat de la lambda | oui | Transformation, vérification null |
| run | this | résultat de la lambda | oui | Calcul avec contexte |
| with | this | résultat de la lambda | non | Regroupement d'appels |
| apply | this | objet de contexte | oui | Initialisation de propriétés |
| also | it | objet de contexte | oui | Effets secondaires, journalisation |
Pour configurer un objet sans retourner de nouvelle valeur, utilisez apply (accès via this) ou also (accès via it). Pour calculer une nouvelle valeur basée sur un objet, utilisez let (accès via it) ou run (accès via this). Pour regrouper des appels sans chaînage, utilisez with. Suivre ce schéma rend le code prévisible pour les autres développeurs familiers avec Kotlin.
apply est une fonction de portée qui retourne l'objet de contexte et fournit un accès à celui-ci via this. C'est l'outil idéal pour initialiser les propriétés d'un objet après sa création. À l'intérieur du bloc apply, les propriétés peuvent être définies sans répéter le nom de l'objet. La méthode retourne l'objet lui-même, ce qui permet d'intégrer apply dans des chaînes ou de l'utiliser dans des initialiseurs.
data class ServerConfig(
var host: String = "localhost",
var port: Int = 8080,
var useTls: Boolean = false
)
val config = ServerConfig().apply {
host = "api.example.com"
port = 443
useTls = true
}
Dans l'exemple, apply configure ServerConfig en accédant directement aux propriétés via this (this est omis). Sans apply, il faudrait écrire config.host = ..., config.port = ... — trois références répétitives à la même variable. Le retour de l'objet permet de sauvegarder le résultat de la configuration dans val config. also, contrairement à apply, passe l'objet via it et convient aux effets secondaires.
fun saveUser(user: User) {
validate(user).also { result ->
println("Résultat de validation : $result")
}
val savedUser = user.also {
log("Saving user ${it.id}")
database.save(it)
}
}
Dans l'exemple, also est utilisé pour la journalisation — tout en retournant l'objet original, il effectue un effet secondaire (affichage d'un message) sans modifier l'objet. C'est une chaîne : validate(user) retourne User, also journalise le résultat et transmet l'utilisateur. also est également utile pour ajouter des éléments à des collections ou pour déboguer au milieu d'une chaîne de transformations.
let est une fonction de portée qui retourne le résultat de la lambda et passe le contexte via it. C'est la fonction principale pour les transformations null-safe : si l'objet est nullable, la combinaison ?.let { } n'exécute le bloc que pour les valeurs non nulles. let est également utilisé pour limiter la portée des variables temporaires et pour les transformations de type map d'un type à un autre.
fun findUser(id: Int): User?
val displayName = findUser(42)?.let { user ->
"${user.name} (${user.email})"
} ?: "Unknown user"
// let avec transformation de collection
val numbers = listOf("1", "2", "3")
val parsed = numbers.firstOrNull()?.let {
it.toIntOrNull()
} ?: 0
Dans le premier exemple, let est appelé via ?. — le bloc n'est exécuté que si findUser a retourné une valeur non nulle. La lambda prend user via it (peut être renommé pour plus de clarté) et retourne une chaîne formatée. Si l'utilisateur n'est pas trouvé, l'opérateur Elvis ?: fournit une valeur par défaut. Dans le deuxième exemple, let convertit une chaîne en nombre avec un null potentiel.
run est un analogue de let avec accès via this, retournant le résultat de la lambda. run est pratique pour les calculs qui nécessitent un contexte d'objet mais retournent une nouvelle valeur. Appeler run() sans objet (comme run { ... }) crée une portée temporaire pour les variables. with est une non-extension avec accès via this, adaptée au regroupement d'appels sans chaînage.
// run comme calcul contextuel
val transformed = listOf(1, 2, 3).run {
filter { it > 1 }.map { it * 10 }
}
println(transformed) // [20, 30]
// with pour regrouper les appels
val info = with(StringBuilder()) {
append("Name: ")
append("Alice")
toString()
}
Dans l'exemple, run sur une liste retourne le résultat du filtrage et du mapping — la transformation elle-même, pas la liste. with prend StringBuilder comme argument (pas via un point) et travaille à l'intérieur avec this : append, append, toString. Le résultat de with est la chaîne info. Le choix entre run et with est souvent subjectif, mais with est préférable lorsque l'objet est passé d'un autre endroit sans chaînage.
with diffère des autres fonctions de portée en ce qu'elle n'est pas une fonction d'extension. Elle prend l'objet comme premier argument et la lambda comme second. À l'intérieur de la lambda, l'objet est accessible via this. with est pratique pour regrouper des appels de méthodes d'objet sans sauvegarder le résultat ou lorsque vous avez besoin d'« entrer » temporairement dans un contexte pour lire plusieurs propriétés.
val user = User("Alice", "alice@example.com", "Admin")
val summary = with(user) {
"User $name has role $role and email $email"
}
println(summary)
// with pour l'initialisation de composants UI
with(TextView(context)) {
text = "Hello"
textSize = 18.0f
setTextColor(Color.BLUE)
}
Dans le premier exemple, with forme la chaîne summary en accédant directement aux propriétés de user via this. Sans with, il faudrait écrire « User ${user.name} has role ${user.role} » — répétition de user. Dans le deuxième exemple, with regroupe la configuration de TextView : toutes les méthodes et propriétés sont appelées sur un seul objet sans répétition. Inconvénient de with — impossibilité de l'utiliser dans une chaîne, car ce n'est pas une extension et elle ne retourne pas l'objet de contexte.
Questions fréquentes
Utilisez apply — il retourne l'objet et fournit un accès via this, permettant de définir les propriétés sans répéter le nom de l'objet. also convient si vous avez besoin d'un paramètre it explicite pour le distinguer des propriétés de l'objet.
let passe le contexte via it (paramètre explicite), run — via this (récepteur implicite). let est pratique pour les vérifications null et les transformations, run — pour les calculs avec contexte. Les deux retournent le résultat de la lambda.
with n'est pas une extension — elle prend l'objet comme argument. Utilisez with pour regrouper des appels lorsque le chaînage n'est pas nécessaire. apply est une extension qui retourne l'objet, pratique dans les initialiseurs et les modèles builder.
Oui, mais soyez prudent avec this — this imbriqué crée de la confusion. Utilisez let ou also avec des noms explicites pour les contextes imbriqués afin d'éviter toute ambiguïté lors de la référence à l'objet externe.
Toutes les fonctions de portée sont inline, donc elles ne créent pas de surcharge de classes anonymes. La différence de performance entre elles est négligeable. Le choix doit être basé sur la lisibilité et les conventions, pas sur la vitesse.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi