Was macht das Schlüsselwort "by" in Kotlin?

82

Während der Entwicklung für Android stoße ich manchmal auf etwas, das so aussieht:

var someModel: someViewModel by notNullAndObservable { vm ->
  ...
}

Ich verstehe nicht, welche Bedeutung das bySchlüsselwort hat.

Kennzeichen
quelle

Antworten:

74

In der Kotlin-Referenz finden Sie zwei Verwendungszwecke für by: Die erste sind delegierte Eigenschaften, die oben verwendet werden:

Es gibt bestimmte gängige Arten von Eigenschaften, die, obwohl wir sie jedes Mal manuell implementieren können, wenn wir sie benötigen, sehr gut ein für alle Mal implementiert und in eine Bibliothek gestellt werden können. Beispiele hierfür sind verzögerte Eigenschaften: Der Wert wird nur beim ersten Zugriff berechnet. Beobachtbare Eigenschaften: Listener werden über Änderungen an dieser Eigenschaft benachrichtigt, wobei Eigenschaften in einer Karte gespeichert werden und nicht jeweils in einem separaten Feld.

Hier delegieren Sie den Getter / Setter an eine andere Klasse, die die Arbeit erledigt und allgemeinen Code enthalten kann. Als weiteres Beispiel unterstützen einige der Abhängigkeitsinjektoren für Kotlin dieses Modell, indem sie den Getter an den Empfang eines Werts aus einer Registrierung von Instanzen delegieren, die von der Abhängigkeitsinjektions-Engine verwaltet werden.

Und die Schnittstellen- / Klassendelegation ist die andere Verwendung:

Das Delegierungsmuster hat sich als gute Alternative zur Implementierungsvererbung erwiesen, und Kotlin unterstützt es nativ, wenn kein Boilerplate-Code erforderlich ist. Eine abgeleitete Klasse kann von einer Schnittstellenbasis erben und alle ihre öffentlichen Methoden an ein angegebenes Objekt delegieren

Hier können Sie eine Schnittstelle an eine andere Implementierung delegieren, sodass die implementierende Klasse nur überschreiben muss, was geändert werden soll, während die übrigen Methoden an eine umfassendere Implementierung zurück delegieren.

Ein Live-Beispiel wären die Klutter Readonly / Immutable-Sammlungen, bei denen sie die spezifische Sammlungsschnittstelle wirklich nur an eine andere Klasse delegieren und dann alles überschreiben, was in der schreibgeschützten Implementierung anders sein muss. Sie sparen viel Arbeit, ohne alle anderen Methoden manuell delegieren zu müssen.

Beide werden in der Kotlin-Sprachreferenz behandelt . Beginnen Sie dort mit den Basisthemen der Sprache.

Jayson Minard
quelle
83

In einfachen Worten können Sie das bySchlüsselwort verstehen, wie es von bereitgestellt wird .

Aus der Sicht des Immobilienkonsumenten valist es etwas, das Getter (get) und varetwas, das Getter und Setter (get, set) hat. Für jede varEigenschaft gibt es einen Standardanbieter für get- und set-Methoden, die wir nicht explizit angeben müssen.

Wenn bySie jedoch ein Schlüsselwort verwenden, geben Sie an, dass dieser Getter / Getter & Setter an anderer Stelle bereitgestellt wird (dh delegiert wurde). Es wird von der Funktion bereitgestellt , die danach kommt by.

Anstatt diese integrierten Methoden zum Abrufen und Festlegen zu verwenden, delegieren Sie diesen Job an eine explizite Funktion.

Ein sehr häufiges Beispiel sind die by lazyEigenschaften für verzögertes Laden. Wenn Sie eine Abhängigkeitsinjektionsbibliothek wie Koin verwenden, werden viele Eigenschaften wie folgt definiert:

var myRepository: MyRepository by inject()  //inject is a function from Koin

In der Klassendefinition folgt es dem gleichen Prinzip, es definiert, wo eine Funktion bereitgestellt wird, aber es kann auf jede Reihe von Methoden / Eigenschaften verweisen, nicht nur auf get und set.

class MyClass: SomeInterface by SomeImplementation, SomeOtherInterface

In diesem Code heißt es: 'Ich bin Klasse MyClass und biete Funktionen der Schnittstelle SomeInterface an, die von SomeImplementation bereitgestellt werden. Ich werde SomeOtherInterface selbst implementieren (das ist implizit, also nein byda). '

Daneejela
quelle
18

Geben Sie hier die Bildbeschreibung ein

Die Syntax lautet:

val/var <property name>: <Type> by <expression>. 

Der Ausdruck nach by ist der Delegat

Wenn wir versuchen, auf den Wert der Eigenschaft p zuzugreifen , dh wenn wir die get () -Methode der Eigenschaft p aufrufen , wird die getValue () -Methode der Delegate- Instanz aufgerufen.

Wenn wir versuchen, den Wert der Eigenschaft p festzulegen , dh wenn wir die set () -Methode der Eigenschaft p aufrufen , wird die setValue () -Methode der Delegate- Instanz aufgerufen.

oiyio
quelle
5

Delegation für Eigentum:

import kotlin.reflect.KProperty

class Delegate {
    // for get() method, ref - a reference to the object from 
    // which property is read. prop - property
    operator fun getValue(ref: Any?, prop: KProperty<*>) = "textA"
    // for set() method, 'v' stores the assigned value
    operator fun setValue(ref: Any?, prop: KProperty<*>, v: String) {
        println("value = $v")
    }
}

object SampleBy {
    var s: String by Delegate() // delegation for property
    @JvmStatic fun main(args: Array<String>) {
        println(s)
        s = "textB"
    }
}

Ergebnis:

textA
value = textB

Delegation für die Klasse:

interface BaseInterface {
    val value: String
    fun f()
}

class ClassA: BaseInterface {
    override val value = "property from ClassA"
    override fun f() { println("fun from ClassA") }
}

// The ClassB can implement the BaseInterface by delegating all public 
// members from the ClassA.
class ClassB(classA: BaseInterface): BaseInterface by classA {}

object SampleBy {
    @JvmStatic fun main(args: Array<String>) {
        val classB = ClassB(ClassA())
        println(classB.value)
        classB.f()
    }
}

Ergebnis:

property from ClassA
fun from ClassA

Delegierung für Parameter:

// for val properties Map is used; for var MutableMap is used
class User(mapA: Map<String, Any?>, mapB: MutableMap<String, Any?>) {
    val name: String by mapA
    val age: Int by mapA
    var address: String by mapB
    var id: Long by mapB
}

object SampleBy {
    @JvmStatic fun main(args: Array<String>) {
        val user = User(mapOf("name" to "John", "age" to 30),
        mutableMapOf("address" to "city, street", "id" to 5000L))

        println("name: ${user.name}; age: ${user.age}; " +
        "address: ${user.address}; id: ${user.id}")
    }
}

Ergebnis:

name: John; age: 30; address: city, street; id: 5000
alexrnov
quelle