Propriétés déléguées à Kotlin

Propriétés déléguées à Kotlin

1. introduction

Le langage de programmation Kotlin prend en charge de manière native les propriétés de classe.

Les propriétés sont généralement directement associées aux champs correspondants, mais il n'est pas toujours nécessaire que cela soit ainsi: tant qu'elles sont correctement exposées au monde extérieur, elles peuvent toujours être considérées comme des propriétés.

Ceci peut être réalisé en manipulant cela dans les getters et les setters, ou en tirant parti de la puissance deDelegates.

2. Que sont les propriétés déléguées?

Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code. Cela permet d'extraire les fonctionnalités déléguées et de les partager entre plusieurs propriétés similaires - par ex. stocker les valeurs de propriété dans une carte au lieu de champs séparés.

Les propriétés déléguées sont utilisées en déclarant la propriété et le délégué utilisé. The by keyword indicates that the property is controlled by the provided delegate instead of its own field.

Par exemple:

class DelegateExample(map: MutableMap) {
    var name: String by map
}

Cela utilise le fait qu'unMutableMap est lui-même un délégué, vous permettant de traiter ses clés comme des propriétés.

3. Propriétés déléguées standard

La bibliothèque standard Kotlin est fournie avec un ensemble de délégués standard prêts à être utilisés.

Nous avons déjà vu un exemple d'utilisation d'unMutableMap pour sauvegarder une propriété mutable. De la même manière, vous pouvez sauvegarder une propriété immuable en utilisant unMap - permettant d'accéder à des champs individuels en tant que propriétés, mais ne les modifiez jamais.

The lazy delegate allows the value of a property to be computed only on first access and then cached. Cela peut être utile pour les propriétés qui peuvent être coûteuses à calculer et dont vous pourriez ne jamais avoir besoin - par exemple, être chargées à partir d'une base de données:

class DatabaseBackedUser(userId: String) {
    val name: String by lazy {
        queryForValue("SELECT name FROM users WHERE userId = :userId", mapOf("userId" to userId)
    }
}

The observable delegate allows for a lambda to be triggered any time the value of the property changes, par exemple permettant des notifications de modification ou la mise à jour d'autres propriétés associées:

class ObservedProperty {
    var name: String by Delegates.observable("") {
        prop, old, new -> println("Old value: $old, New value: $new")
    }
}

4. Création de vos délégués

Il y aura des moments où vous voudrez écrire à vos délégués, plutôt que d'utiliser ceux qui existent déjà. This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.

Ces deux interfaces définissent une méthode appeléegetValue - qui est utilisée pour fournir la valeur actuelle de la propriété déléguée lorsqu'elle est lue. Cela prend deux arguments et retourne la valeur de la propriété:

  • thisRef - une référence à la classe dans laquelle se trouve la propriété

  • property - une description de réflexion de la propriété déléguée

L'interfaceReadWriteProperty définit en outre une méthode appeléesetValue qui est utilisée pour mettre à jour la valeur actuelle de la propriété lorsqu'elle est écrite. Cela prend trois arguments et n'a aucune valeur de retour:

  • thisRef - Une référence à la classe dans laquelle se trouve la propriété

  • property - Une description de réflexion de la propriété déléguée

  • value - La nouvelle valeur de la propriété

À titre d'exemple, écrivons un délégué qui fonctionne toujours en ce qui concerne une connexion à une base de données au lieu des champs locaux:

class DatabaseDelegate(readQuery: String, writeQuery: String, id: Any) : ReadWriteDelegate {
    fun getValue(thisRef: R, property: KProperty<*>): T {
        return queryForValue(readQuery, mapOf("id" to id))
    }

    fun setValue(thisRef: R, property: KProperty<*>, value: T) {
        update(writeQuery, mapOf("id" to id, "value" to value))
    }
}

Cela dépend de deux fonctions de niveau supérieur pour accéder à la base de données:

  • queryForValue - cela prend du SQL et des liaisons et renvoie la première valeur

  • update - cela prend du SQL et des liaisons et le traite comme une instruction UPDATE

Nous pouvons ensuite utiliser cela comme n'importe quel délégué ordinaire et faire en sorte que notre classe soit automatiquement sauvegardée par la base de données:

class DatabaseUser(userId: String) {
    var name: String by DatabaseDelegate(
      "SELECT name FROM users WHERE userId = :id",
      "UPDATE users SET name = :value WHERE userId = :id",
      userId)
    var email: String by DatabaseDelegate(
      "SELECT email FROM users WHERE userId = :id",
      "UPDATE users SET email = :value WHERE userId = :id",
      userId)
}

5. Sommaire

La délégation de propriétés est une technique puissante, qui vous permet d'écrire du code qui prend le contrôle d'autres propriétés et facilite le partage de cette logique entre différentes classes. Cela permet une logique robuste et réutilisable qui ressemble à un accès normal aux propriétés.

Un exemple pleinement fonctionnel pour cet article peut être trouvéover on GitHub.