Analyse comparative des fonctions de portée Kotlin : run, apply, let, also et with

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

Étiquettes: kotlin JVM Software-Architecture Syntax

Publié le 19 juillet à 01h32