A cosa serve Kotlin Backing Field?


92

Come sviluppatore Java, il concetto di un campo di supporto mi è un po 'estraneo. Dato:

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

A cosa serve questo campo di supporto? I documenti di Kotlin hanno detto:

Le classi in Kotlin non possono avere campi. Tuttavia, a volte è necessario disporre di un campo di supporto quando si utilizzano funzioni di accesso personalizzate .

Perché? Qual è la differenza con l'utilizzo del nome stesso delle proprietà all'interno del setter, ad es. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}

17
L'uso della proprietà stessa nel setter risulterà in una ricorsione infinita poiché l'assegnazione di un valore alla proprietà chiamerà sempre il setter.
funglejunk

1
@ Strelok mio male .... stavo assumendo che this.counter = valueè lo stesso con l'equivalente Java durante la lettura dei documenti di Kotlin.
Yudhistira Arya

4
Questo articolo riguarda Field vs Property.
avinash

Risposte:


86

Perché, ad esempio, se non hai una fieldparola chiave, non sarai in grado di impostare / ottenere effettivamente il valore in get()o set(value). Consente di accedere al campo di supporto nelle funzioni di accesso personalizzate.

Questo è il codice Java equivalente del tuo campione:

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

Apparentemente questo non va bene, poiché il setter è solo un'infinita ricorsione in se stesso, che non cambia mai nulla. Ricorda in kotlin ogni volta che scrivi foo.bar = valueverrà tradotto in una chiamata setter invece che in un file PUTFIELD.


EDIT: Java ha campi mentre Kotlin ha proprietà , che è un concetto di livello piuttosto superiore rispetto ai campi.

Esistono due tipi di proprietà: una con un campo sottostante, una senza.

Una proprietà con un campo di supporto memorizzerà il valore sotto forma di campo. Quel campo rende possibile la memorizzazione del valore in memoria. Un esempio di tale proprietà sono le proprietà firste seconddi Pair. Quella proprietà cambierà la rappresentazione in memoria di Pair.

Una proprietà senza un campo di supporto dovrà memorizzare il proprio valore in modi diversi dall'archiviazione diretta in memoria. Deve essere calcolato da altre proprietà o dall'oggetto stesso. Un esempio di tale proprietà è la indicesproprietà extension di List, che non è supportata da un campo, ma un risultato calcolato in base alla sizeproprietà. Quindi non cambierà la rappresentazione in memoria di List(cosa che non può fare affatto perché Java è tipizzato staticamente).


Grazie per la risposta! Colpa mia ... stavo supponendo che this.counter = valuesia lo stesso con l'equivalente Java.
Yudhistira Arya

Documentato ovunque? Grazie :)
Alston


1
Molte spiegazioni confuse. Perché non possiamo semplicemente dire che a fieldè più simile a un puntatore o un riferimento a una variabile membro esistente. Poiché get/setsegue immediatamente counterquindi, la fieldparola chiave è un riferimento a counter. Destra?
Eigenfield

@typelogic questa risposta è più adatta per i programmatori con sfondi Java / JS (allora non esisteva Kotlin / Native), non C / C ++. Quello che trovi sfocato è pane e burro per altre persone.
glee8e

31

All'inizio anche io ho avuto difficoltà a capire questo concetto. Quindi lascia che te lo spieghi con l'aiuto di un esempio.

Considera questa classe di Kotlin

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

Ora, quando guardiamo il codice, possiamo vedere che ha 2 proprietà cioè - size(con funzioni di accesso predefinite) e isEmpty(con funzioni di accesso personalizzate). Ma ha solo 1 campo cioè size. Per capire che ha solo 1 campo, vediamo l'equivalente Java di questa classe.

Vai su Strumenti -> Kotlin -> Mostra Kotlin ByteCode in Android Studio. Fare clic su Decompile.

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

Chiaramente possiamo vedere che la classe java ha solo funzioni getter e setter per isEmpty, e non vi è alcun campo dichiarato per essa. Allo stesso modo in Kotlin, non esiste un campo di supporto per la proprietà isEmpty, poiché la proprietà non dipende affatto da quel campo. Quindi nessun campo di supporto.


Ora rimuoviamo il getter personalizzato e il setter della isEmptyproprietà.

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

E l'equivalente Java della classe precedente è

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

Qui vediamo sia i campi sizeche isEmpty. isEmptyè un campo di supporto perché il getter e il setter per la isEmptyproprietà dipendono da esso.


4
Buona spiegazione. Grazie
Sonu Sanjeev

1
Grazie davvero per la spiegazione. Sono arrivato a Kotlin anche da Java e il concetto di proprietà è nuovo per me. Ma l'ho capito, grazie a te e alle guide. :)
Yamashiro Rion

Dio ti benedica.
Andrea Cioccarelli

Mi piace questa risposta, si citano onestamente i fatti. Sono ancora in dubbio, perché C # non ha bisogno di fieldparole chiave, è possibile che un miglioramento del linguaggio di Kotlin rimuoverà questa strana fieldparola chiave ed eviterà che le anime indifese cadano nell'abisso della ricorsione infinita?
Eigenfield

9

I campi di supporto sono utili per eseguire la convalida o attivare eventi al cambio di stato. Pensa alle volte in cui hai aggiunto codice a un setter / getter Java. I campi di supporto sarebbero utili in scenari simili. Utilizzeresti i campi di supporto quando avevi bisogno di controllare o avere visibilità su setter / getter.

Quando assegni il campo con il nome del campo stesso, stai effettivamente invocando il setter (cioè set(value)). Nell'esempio che hai, this.counter = valuericorrerebbe a set (value) fino a quando non supereremo il nostro stack. L'utilizzo fieldbypassa il codice setter (o getter).


Siamo spiacenti, ma la tua spiegazione contiene il termine che deve essere spiegato. E poi, prima hai citato uno scenario Java e poi all'improvviso senza preavviso hai cambiato le righe in un'effettiva dichiarazione Kotlin. La necessità di Kotlin per la parola chiave fieldnon è in C #, quindi abbiamo bisogno di una spiegazione migliore di quella che hai citato qui.
eigenfield

2

La mia comprensione sta usando l' identificatore di campo come riferimento al valore della proprietà in get o set , quando si desidera modificare o utilizzare il valore della proprietà in get o set .

Per esempio:

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

Poi:

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)

0

La terminologia backing fieldè piena di mistero. La parola chiave utilizzata è field. I get/setmetodi seguono immediatamente accanto alla variabile membro che sta per essere ottenuta o impostata tramite questo meccanismo di metodi di protezione della porta. La fieldparola chiave si riferisce solo alla variabile membro che deve essere impostata o ottenuta . Al momento Kotlin, non è possibile fare riferimento alla variabile membro direttamente all'interno dei metodi get o set della porta di protezione perché sfortunatamente risulterà in una ricorsione infinita perché richiamerà nuovamente il get o set e quindi il runtime scende nell'abisso profondo.

In C # , tuttavia, è possibile fare riferimento direttamente alla variabile membro all'interno dei metodi getter / setter. Sto citando questo confronto per presentare l'idea che questa fieldparola chiave sia il modo in cui l'attuale Kotlin la sta implementando, ma spero che venga rimossa nelle versioni successive e ci consenta di fare riferimento direttamente alla variabile membro direttamente senza risultare in una ricorsione infinita.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.