Delegierte Eigenschaften in Kotlin

Delegierte Immobilien in Kotlin

1. Einführung

Die Programmiersprache Kotlin bietet native Unterstützung für Klasseneigenschaften.

Eigenschaften werden in der Regel direkt durch entsprechende Felder gesichert, dies muss jedoch nicht immer der Fall sein. Solange sie ordnungsgemäß der Außenwelt ausgesetzt sind, können sie weiterhin als Eigenschaften betrachtet werden.

Dies kann erreicht werden, indem dies in Gettern und Setzern gehandhabt wird oder indem die Leistung vonDelegates. genutzt wird

2. Was sind delegierte Eigenschaften?

Simply put, delegated properties are not backed by a class field and delegate getting and setting to another piece of code. Dies ermöglicht es, delegierte Funktionen zu abstrahieren und von mehreren ähnlichen Eigenschaften gemeinsam zu nutzen - z. Speichern von Eigenschaftswerten in einer Karte anstelle von separaten Feldern.

Delegierte Eigenschaften werden verwendet, indem die Eigenschaft und der von ihr verwendete Delegat deklariert werden. The by keyword indicates that the property is controlled by the provided delegate instead of its own field.

Zum Beispiel:

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

Dies nutzt die Tatsache, dass einMutableMap selbst ein Delegat ist, sodass Sie seine Schlüssel als Eigenschaften behandeln können.

3. Delegierte Standardeigenschaften

Die Kotlin-Standardbibliothek enthält eine Reihe von Standarddelegaten, die sofort verwendet werden können.

Wir haben bereits ein Beispiel für die Verwendung vonMutableMap zum Sichern einer veränderlichen Eigenschaft gesehen. Auf die gleiche Weise können Sie eine unveränderliche Eigenschaft mitMap sichern. So können Sie auf einzelne Felder als Eigenschaften zugreifen, diese jedoch nie ändern.

The lazy delegate allows the value of a property to be computed only on first access and then cached. Dies kann nützlich sein für Eigenschaften, deren Berechnung möglicherweise teuer ist und die Sie möglicherweise nie benötigen - zum Beispiel beim Laden aus einer Datenbank:

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, um beispielsweise Änderungsbenachrichtigungen oder die Aktualisierung anderer verwandter Eigenschaften zu ermöglichen:

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

4. Erstellen Sie Ihre Delegierten

Es wird Zeiten geben, in denen Sie Ihre Delegierten schreiben möchten, anstatt bereits vorhandene zu verwenden. This relies on writing a class that extends one of two interfaces – ReadOnlyProperty or ReadWriteProperty.

Beide Schnittstellen definieren eine Methode namensgetValue, mit der der aktuelle Wert der delegierten Eigenschaft beim Lesen angegeben wird. Dies akzeptiert zwei Argumente und gibt den Wert der Eigenschaft zurück:

  • thisRef - ein Verweis auf die Klasse, in der sich die Eigenschaft befindet

  • property - eine Reflexionsbeschreibung der zu delegierenden Eigenschaft

DieReadWriteProperty-Schnittstelle definiert zusätzlich eine Methode namenssetValue, mit der der aktuelle Wert der Eigenschaft beim Schreiben aktualisiert wird. Dies erfordert drei Argumente und hat keinen Rückgabewert:

  • thisRef - Ein Verweis auf die Klasse, in der sich die Eigenschaft befindet

  • property - Eine Reflexionsbeschreibung der zu delegierenden Eigenschaft

  • value - Der neue Wert der Eigenschaft

Schreiben wir als Beispiel einen Delegaten, der immer in Bezug auf eine Datenbankverbindung anstelle lokaler Felder funktioniert:

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))
    }
}

Dies hängt von zwei Funktionen der obersten Ebene ab, um auf die Datenbank zuzugreifen:

  • queryForValue - Dies erfordert etwas SQL und einige Bindungen und gibt den ersten Wert zurück

  • update - Dies erfordert etwas SQL und einige Bindungen und behandelt es als UPDATE-Anweisung

Wir können dies dann wie einen gewöhnlichen Delegaten verwenden und unsere Klasse automatisch von der Datenbank sichern lassen:

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. Zusammenfassung

Die Eigenschaftendelegierung ist eine leistungsstarke Technik, mit der Sie Code schreiben können, der die Kontrolle über andere Eigenschaften übernimmt, und mit der diese Logik problemlos von verschiedenen Klassen gemeinsam genutzt werden kann. Dies ermöglicht eine robuste, wiederverwendbare Logik, die aussieht und sich anfühlt wie ein regulärer Eigenschaftenzugriff.

Ein voll funktionsfähiges Beispiel für diesen Artikel finden Sie inover on GitHub.