Pourquoi utiliser les delegates en Kotlin ?

Les delegates sont une façon de dire à Kotlin : « Cette propriété ou cette interface, un autre objet s’en occupe à ma place. »
Cela permet d’éviter du code répétitif et de rendre ton code plus propre et plus facile à maintenir.

Qu’est-ce qu’un delegate de propriété ?

Normalement, quand tu veux ajouter du comportement à une propriété (validation, logs, transformation), tu dois écrire des getters et setters. Avec un delegate, tu déplaces cette logique dans une classe séparée et tu la réutilises partout.


var username: String by UsernameDelegate()

Ici, UsernameDelegate décide comment la valeur est stockée, modifiée ou contrôlée. De ton côté, tu utilises username normalement, sans te soucier de la logique interne.

Les delegates les plus utiles de Kotlin

Kotlin offre déjà plusieurs delegates prêts à l’emploi, très pratiques.

  • lazy : crée la valeur seulement quand elle est utilisée pour la première fois.
  • observable : déclenche une fonction chaque fois que la valeur change.
  • vetoable : permet d’accepter ou non la nouvelle valeur.
  • notNull : oblige à donner une valeur avant la première lecture.

Exemple simple : lazy


val userRepository by lazy {
    // Code exécuté uniquement quand userRepository est utilisé pour la première fois
    DefaultUserRepository(api, cache)
}

Idéal quand l’objet est long à créer mais ne sera pas toujours utilisé.

Exemple simple : observable


var theme: Theme by Delegates.observable(Theme.SYSTEM) { _, old, new ->
    println("Le thème change de $old à $new")
}

Très utile pour mettre à jour une interface dès qu’une valeur change.

Créer un delegate personnalisé

Tu peux créer ton propre delegate. Cela te permet d’ajouter automatiquement un comportement commun à plusieurs propriétés.


class TrimmingStringDelegate(var value: String = "") {

    operator fun getValue(thisRef: Any?, property: KProperty<*>): String {
        return value
    }

    operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: String) {
        // Enlève les espaces inutiles automatiquement
        value = newValue.trim()
    }
}

class ProfileForm {
    var displayName: String by TrimmingStringDelegate()
}

Dans cet exemple, chaque fois que tu assignes un texte, les espaces inutiles au début et à la fin sont automatiquement supprimés.

Délégation d’interface : déléguer tout un comportement

Avec Kotlin, tu peux aussi déléguer toute l’implémentation d’une interface à un autre objet. C’est très pratique pour ajouter une petite fonctionnalité à une classe sans copier tout son code.


class LoggingUserRepository(
    private val repo: UserRepository
) : UserRepository by repo {

    override suspend fun getUser(id: UserId): User {
        println("Chargement de l'utilisateur $id")
        return repo.getUser(id)
    }
}

Ici, LoggingUserRepository récupère automatiquement toutes les méthodes de UserRepository grâce à by repo, et ne réécrit que ce qui l'intéresse (ici, ajouter un log).

Quand utiliser un delegate ?

  • Quand plusieurs propriétés ont le même comportement (ex. validation).
  • Quand tu veux réduire le code répétitif.
  • Quand tu veux améliorer la testabilité en injectant d’autres comportements pour les tests.

Quand éviter ?

  • Quand un simple getter/setter est suffisant.
  • Quand cela rend le code moins lisible.

En résumé, les delegates Kotlin t’aident à écrire moins de code, à éviter la duplication et à structurer proprement tes comportements transverses (validation, logs, transformation…). C’est un outil très puissant pour garder ton code clair et évolutif.