Wofür ist Kotlin Backing Field?

92

Als Java-Entwickler ist mir das Konzept eines Hintergrundfeldes etwas fremd. Gegeben:

class Sample {
    var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value
    }
}

Wofür ist dieses Hintergrundfeld gut? Kotlin-Dokumente sagten:

Klassen in Kotlin können keine Felder haben. Manchmal ist es jedoch erforderlich, ein Hintergrundfeld zu haben, wenn benutzerdefinierte Accessoren verwendet werden .

Warum? Was ist der Unterschied zur Verwendung des Eigenschaftsnamens selbst im Setter, z. B. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
quelle
16
Die Verwendung der Eigenschaft selbst im Setter führt zu einer endlosen Rekursion, da das Zuweisen eines Werts zur Eigenschaft den Setter immer aufruft.
funglejunk
1
@ Strelok my bad .... Ich ging davon aus, dass this.counter = valuedies beim Lesen von Kotlins Dokumenten mit dem Java-Äquivalent identisch ist.
Yudhistira Arya
3
Dieser Artikel ist für Field vs Property.
Avinash

Antworten:

86

Wenn Sie beispielsweise kein fieldSchlüsselwort haben, können Sie den Wert im get()oder nicht tatsächlich festlegen / abrufen set(value). Sie können auf das Hintergrundfeld in den benutzerdefinierten Accessoren zugreifen.

Dies ist der äquivalente Java-Code Ihres Beispiels:

class Sample {
    private int counter = 0;
    public void setCounter(int value) {
        if (value >= 0) setCounter(value);
    }
    public int getCounter() {
        return counter;
    }
}

Anscheinend ist dies nicht gut, da der Setter nur eine unendliche Rekursion in sich selbst ist und niemals etwas ändert. Denken Sie daran, dass in Kotlin jedes Mal, wenn Sie schreiben, foo.bar = valuees in einen Setter-Aufruf anstelle von a übersetzt wird PUTFIELD.


BEARBEITEN: Java hat Felder, während Kotlin Eigenschaften hat , was ein eher übergeordnetes Konzept als Felder ist.

Es gibt zwei Arten von Eigenschaften: eine mit Hintergrundfeld, eine ohne.

Eine Eigenschaft mit einem Hintergrundfeld speichert den Wert in Form eines Feldes. Dieses Feld ermöglicht das Speichern von Werten im Speicher. Ein Beispiel für eine solche Eigenschaft sind die firstund secondEigenschaften von Pair. Diese Eigenschaft ändert die speicherinterne Darstellung von Pair.

Eine Eigenschaft ohne Hintergrundfeld muss ihren Wert auf andere Weise als direkt im Speicher speichern. Es muss aus anderen Eigenschaften oder dem Objekt selbst berechnet werden. Ein Beispiel für eine solche Eigenschaft ist die indicesErweiterungseigenschaft von List, die nicht durch ein Feld unterstützt wird, sondern ein auf der sizeEigenschaft basierendes berechnetes Ergebnis . Daher wird die speicherinterne Darstellung von nicht geändert List(was überhaupt nicht möglich ist, da Java statisch typisiert ist).

glee8e
quelle
Danke für die Antwort! Mein schlechtes ... Ich ging davon aus, dass this.counter = valuedies auch mit Java-Äquivalenten der Fall ist.
Yudhistira Arya
Überall dokumentiert? Danke :)
Alston
@Alston, hier ist die Dokumentation: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Viele unscharfe Erklärungen. Warum können wir nicht einfach sagen, dass a fieldeher ein Zeiger oder ein Verweis auf eine vorhandene Mitgliedsvariable ist? Da das also get/setunmittelbar folgt counter, ist das fieldSchlüsselwort ein Verweis auf counter. Richtig?
Truthadjustr
@typelogic Diese Antwort ist am besten auf Programmierer mit Java / JS-Hintergrund zugeschnitten (damals gab es kein Kotlin / Native), nicht auf C / C ++. Was Sie unscharf finden, ist Brot und Butter für einige andere Leute.
Glee8e
28

Anfangs hatte auch ich Schwierigkeiten, dieses Konzept zu verstehen. Lassen Sie es mich Ihnen anhand eines Beispiels erklären.

Betrachten Sie diese Kotlin-Klasse

class DummyClass {
    var size = 0;
    var isEmpty
        get() = size == 0
        set(value) {
            size = size * 2
        }
}

Wenn wir uns nun den Code ansehen, können wir sehen, dass er zwei Eigenschaften hat, nämlich - size(mit Standard-Accessoren) und isEmpty(mit benutzerdefinierten Accessoren). Aber es hat nur 1 Feld dh size. Um zu verstehen, dass es nur ein Feld gibt, sehen wir uns das Java-Äquivalent dieser Klasse an.

Gehen Sie zu Extras -> Kotlin -> Kotlin ByteCode in Android Studio anzeigen. Klicken Sie auf Dekompilieren.

   public final class DummyClass {
   private int size;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.size == 0;
   }

   public final void setEmpty(boolean value) {
      this.size *= 2;
   }
}

Wir können deutlich sehen, dass die Java-Klasse nur Getter- und Setter-Funktionen für isEmptyhat und kein Feld dafür deklariert ist. Ebenso gibt es in Kotlin kein Hintergrundfeld für die Eigenschaft isEmpty, da die Eigenschaft überhaupt nicht von diesem Feld abhängt. Also kein Hintergrundfeld.


Lassen Sie uns nun den benutzerdefinierten Getter und Setter der isEmptyEigenschaft entfernen .

class DummyClass {
    var size = 0;
    var isEmpty = false
}

Und das Java-Äquivalent der obigen Klasse ist

public final class DummyClass {
   private int size;
   private boolean isEmpty;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.isEmpty;
   }

   public final void setEmpty(boolean var1) {
      this.isEmpty = var1;
   }
}

Hier sehen wir sowohl die Felder sizeals auch isEmpty. isEmptyist ein Hintergrundfeld, da der Getter und der Setter für die isEmptyEigenschaft davon abhängen.

der Passagier
quelle
4
Gute Erklärung. Vielen Dank
Sonu Sanjeev
1
Wirklich, danke für die Erklärung. Ich bin auch von Java nach Kotlin gekommen, und das Konzept der Eigenschaften ist für mich neu. Aber ich habe es verstanden, dank dir und den Führern. :)
Yamashiro Rion
Gott kann dich segnen.
Andrea Cioccarelli
Ich mag diese Antwort, es wird ehrlich die Fakten zitiert. Ich bin immer noch im Zweifel, weil C # kein fieldSchlüsselwort benötigt. Ist es möglich, dass eine Sprachverbesserung von Kotlin dieses seltsame fieldSchlüsselwort entfernt und verhindert, dass die hilflosen Seelen in den Abgrund einer unendlichen Rekursion fallen?
Truthadjustr
9

Sicherungsfelder eignen sich zum Ausführen der Validierung oder zum Auslösen von Ereignissen bei Statusänderungen. Denken Sie an die Zeiten, in denen Sie einem Java-Setter / Getter Code hinzugefügt haben. Sicherungsfelder wären in ähnlichen Szenarien nützlich. Sie würden Hintergrundfelder verwenden, wenn Sie Setter / Getter steuern oder sichtbar machen müssen.

Wenn Sie dem Feld den Feldnamen selbst zuweisen, rufen Sie tatsächlich den Setter auf (dh set(value)). In dem Beispiel, das Sie haben, this.counter = valuewürde in set (Wert) zurückkehren, bis wir unseren Stapel überlaufen. Mit wird fieldder Setter- (oder Getter-) Code umgangen.

Mark Mucha
quelle
Entschuldigung, aber Ihre Erklärung enthält den Begriff, der erklärt werden muss. Und dann haben Sie zuerst ein Java-Szenario zitiert und dann plötzlich ohne Vorwarnung die Lains auf eine tatsächliche Kotlin-Anweisung umgestellt. Das Kotlin-Bedürfnis für das Schlüsselwort fieldist nicht in C #, daher brauchen wir eine bessere Erklärung als die hier zitierte.
Truthadjustr
2

Mein Verständnis ist , unter Verwendung von Feldkennung als Verweis auf den Wert der Eigenschaft in get oder set , wenn Sie ändern möchten oder den Wert der Eigenschaft in verwenden get oder set .

Beispielsweise:

class A{
    var a:Int=1
        get(){return field * 2}    // Similiar to Java: public int geta(){return this.a * 2}
        set(value) {field = value + 1}
}

Dann:

var t = A()
println(t.a)    // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2         // The real action is similar to Java code: t.a = t.a +1
println(t.a)    // OUTPUT: 6, equal to Java code: println(t.a * 2)
Freddie
quelle
0

Die Terminologie backing fieldist voller Geheimnisse. Das verwendete Schlüsselwort ist field. Die get/setMethoden folgen unmittelbar neben der Elementvariablen, die über diesen Mechanismus für Türschutzmethoden abgerufen oder eingestellt werden soll. Das fieldSchlüsselwort bezieht sich nur auf die Mitgliedsvariable, die gesetzt oder abgerufen werden soll . Derzeit Kotlin, können Sie nicht auf die Membervariable beziehen sich direkt in der get oder set Schutztür Methoden , weil es leider zu Endlosschleife führen wird , weil es wird wieder invoke der get oder Satz und damit LEDs Auf der Laufzeit nach unten in den tiefen Abgrund.

In C # können Sie jedoch direkt auf die Elementvariable innerhalb der Getter / Setter-Methoden verweisen. Ich zitiere diesen Vergleich, um die Idee zu präsentieren, dass dieses fieldSchlüsselwort so ist, wie der gegenwärtige Kotlin es implementiert, aber ich hoffe, dass es in späteren Versionen entfernt wird und es uns ermöglicht, die Mitgliedsvariable direkt zu referenzieren, ohne dass es zu einer unendlichen Rekursion kommt.

trueadjustr
quelle