En Kotlin, les fonctions de portée (scope functions) sont des outils essentiels pour écrire un code plus concis et lisible. Bien qu'elles partagent des similitudes, leur distinction repose sur deux critères principaux : la manière de référencer l'objet (this ou it) et la valeur de retour.
1. Définitions techniques
Voici les signatures standards de ces fonctions dans la bibliothèque standard de Kotlin :
// Fonctions d'extension retournant le résultat du bloc
public inline fun <T, R> T.run(block: T.() -> R): R = block()
public inline fun <T, R> T.let(block: (T) -> R): R = block(this)
// Fonctions d'extension retournant l'objet récepteur lui-même
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
public inline fun <T> T.also(block: (T) -> Unit): T {
block(this)
return this
}
// Fonctions globales (non-extensions)
public inline fun <R> run(block: () -> R): R = block()
public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
2. Comparaison entre let et run
Ces deux fonctiosn retournent le résultat de la dernière expression du bloc lambda. La différence majeure réside dans l'accès à l'objet : run utilise le récepteur implicite this, tandis que let passe l'objet en arguement it.
L'usage de let est particulièrement répandu pour les vérifications de nullité avec l'opérateur ?..
val name: String? = "Kotlin"
// Utilisation de let pour la sécurité
name?.let {
println("La longueur du texte est ${it.length}")
}
// Enchaînement pour transformer une donnée
val formattedStatus = " active ".run {
this.trim().uppercase()
}.let {
"Status: $it"
}
println(formattedStatus) // Affiche: Status: ACTIVE
3. Comparaison entre apply et also
Contrairement aux précédentes, ces fonctions retournent l'objet d'origine, ignorant le résultat du bloc lambda. Elles sont idéales pour les opérations seconadires ou la configuration d'objets.
- apply : Utilisé principalement pour initialiser les propriétés d'un objet (accès via
this). - also : Préféré pour les actions de bord comme la journalisation ou la validation (accès via
it).
class WebServer {
var port: Int = 80
var host: String = "localhost"
}
// Configuration avec apply
val server = WebServer().apply {
port = 8080
host = "127.0.0.1"
}
// Action supplémentaire avec also
val userList = mutableListOf("Alice", "Bob").also {
println("Liste créée avec ${it.size} éléments")
}
4. Fonctions globales : with et run
Ces fonctions ne sont pas appelées sur une instance via un point, mais encapsulent une logique spécifique.
run (global) : Utile pour isoler un bloc de code et limiter la portée des variables temporaires tout en retournant un résultat.
val sessionToken = run {
val prefix = "SECURE"
val timestamp = System.currentTimeMillis()
"$prefix-$timestamp"
}
with : Recommandé pour appeler plusieurs méthodes sur un même objet sans répéter son nom. Notez qu'il n'est pas sécurisé contre les valeurs nulles contrairement à un appel d'extension.
val settings = StringBuilder()
with(settings) {
append("Mode: Debug\n")
append("Version: 1.2.0")
toString()
}
Synthèse des différences
| Fonction | Référence à l'objet | Valeur de retour |
|---|---|---|
let |
it |
Résultat du lambda |
run |
this |
Résultat du lambda |
apply |
this |
L'objet lui-même |
also |
it |
L'objet lui-même |
with |
this |
Résultat du lambda |