À quoi sert le Backing Field Kotlin?

92

En tant que développeur Java, le concept de backing field m'est un peu étranger. Donné:

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

À quoi sert ce champ de support? Les documents de Kotlin ont déclaré:

Les classes à Kotlin ne peuvent pas avoir de champs. Cependant, il est parfois nécessaire d'avoir un champ de sauvegarde lors de l'utilisation d'accesseurs personnalisés .

Pourquoi? Quelle est la différence avec l'utilisation du nom des propriétés lui-même dans le setter, par exemple. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
la source
17
L'utilisation de la propriété elle-même dans le setter se traduira par une récursivité sans fin car l'affectation d'une valeur à la propriété appellera toujours le setter.
funglejunk
1
@Strelok mon mauvais .... Je supposais que c'était this.counter = valuela même chose avec l'équivalent Java lors de la lecture de la documentation de Kotlin.
Yudhistira Arya
4
Cet article concerne Field vs Property.
avinash

Réponses:

86

Parce que, disons que si vous n'avez pas de fieldmot - clé, vous ne pourrez pas réellement définir / obtenir la valeur dans get()ou set(value). Il vous permet d'accéder au champ de sauvegarde dans les accesseurs personnalisés.

C'est le code Java équivalent de votre exemple:

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

Apparemment, ce n'est pas bon, car le setter n'est qu'une récursion infinie en lui-même, ne changeant jamais rien. Rappelez-vous que dans kotlin, chaque fois que vous écrivez, foo.bar = valueil sera traduit en un appel de setter au lieu d'un PUTFIELD.


EDIT: Java a des champs tandis que Kotlin a des propriétés , ce qui est un concept de niveau plus élevé que les champs.

Il existe deux types de propriétés: une avec un champ de sauvegarde, une sans.

Une propriété avec un champ de sauvegarde stockera la valeur sous la forme d'un champ. Ce champ permet de stocker la valeur en mémoire. Un exemple d'une telle propriété est les propriétés firstet secondde Pair. Cette propriété modifiera la représentation en mémoire de Pair.

Une propriété sans champ de sauvegarde devra stocker sa valeur autrement que de la stocker directement en mémoire. Il doit être calculé à partir d'autres propriétés ou de l'objet lui-même. Un exemple d'une telle propriété est la indicespropriété d'extension de List, qui n'est pas soutenue par un champ, mais un résultat calculé basé sur la sizepropriété. Cela ne changera donc pas la représentation en mémoire de List(ce qu'il ne peut pas faire du tout car Java est typé statiquement).

glee8e
la source
Merci d'avoir répondu! Mon mauvais ... je supposais que this.counter = valuec'est la même chose avec l'équivalent java.
Yudhistira Arya
N'importe où documenté? Merci :)
Alston
@Alston, voici la documentation: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Beaucoup d'explications floues. Pourquoi ne pouvons-nous pas simplement dire que a fieldressemble plus à un pointeur ou à une référence à une variable membre existante. Puisque le suivant get/setsuit counterdonc immédiatement , le fieldmot-clé est une référence à counter. Droite?
eigenfield
@typelogic cette réponse est plus adaptée aux programmeurs avec des arrière-plans Java / JS (à l'époque, il n'y a pas de Kotlin / Native), pas C / C ++. Ce que vous trouvez flou, c'est du pain et du beurre pour d'autres personnes.
glee8e
31

Au début, j'ai aussi eu du mal à comprendre ce concept. Alors laissez-moi vous l'expliquer à l'aide d'un exemple.

Considérez cette classe Kotlin

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

Maintenant, quand nous regardons le code, nous pouvons voir qu'il a 2 propriétés à savoir - size(avec les accesseurs par défaut) et isEmpty(avec les accesseurs personnalisés). Mais il n'a qu'un seul champ ie size. Pour comprendre qu'elle n'a qu'un seul champ, voyons l'équivalent Java de cette classe.

Allez dans Outils -> Kotlin -> Afficher Kotlin ByteCode dans Android Studio. Cliquez sur Décompiler.

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

Nous pouvons clairement voir que la classe java n'a que des fonctions getter et setter pour isEmpty, et il n'y a pas de champ déclaré pour cela. De même, dans Kotlin, il n'y a pas de champ de sauvegarde pour la propriété isEmpty, car la propriété ne dépend pas du tout de ce champ. Donc pas de champ de support.


Supprimons maintenant le getter et le setter de isEmptypropriété personnalisés.

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

Et l'équivalent Java de la classe ci-dessus est

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

Ici, nous voyons à la fois les champs sizeet isEmpty. isEmptyest un champ de sauvegarde car le getter et le setter pour la isEmptypropriété en dépendent.

le passager noir
la source
4
Bonne explication. Merci
Sonu Sanjeev
1
Vraiment, merci pour l'explication. Je suis également arrivé à Kotlin depuis Java, et le concept de propriétés est nouveau pour moi. Mais je l'ai compris, grâce à vous et aux guides. :)
Yamashiro Rion
Dieu peut vous bénir.
Andrea Cioccarelli
J'aime cette réponse, il est honnêtement cité les faits. Je suis toujours dans le doute, car C # n'a pas besoin de fieldmot-clé est-il possible qu'une amélioration du langage de Kotlin supprime ce fieldmot-clé étrange et évite aux âmes impuissantes de tomber dans l'abîme de la récursivité infinie?
eigenfield
9

Les champs de sauvegarde sont utiles pour exécuter la validation ou déclencher des événements lors d'un changement d'état. Pensez aux fois où vous avez ajouté du code à un setter / getter Java. Les champs de sauvegarde seraient utiles dans des scénarios similaires. Vous utiliseriez des champs de sauvegarde lorsque vous avez besoin de contrôler ou d'avoir une visibilité sur les setters / getters.

Lorsque vous attribuez le champ avec le nom du champ lui-même, vous invoquez en fait le setter (ie set(value)). Dans l'exemple que vous avez, this.counter = valuerecurse dans set (valeur) jusqu'à ce que nous débordions de notre pile. L'utilisation fieldcontourne le code du setter (ou du getter).

Mark Mucha
la source
Désolé, mais votre explication contient le terme qui doit être expliqué. Et puis, vous avez d'abord cité un scénario Java, puis soudainement, sans avertissement, vous passez à une instruction Kotlin réelle. Le besoin de Kotlin pour le mot field- clé n'est pas en C # , nous avons donc besoin d'une meilleure explication que celle que vous avez citée ici.
eigenfield
2

Ma compréhension utilise l' identificateur de champ comme référence à la valeur de la propriété dans get ou set , lorsque vous souhaitez modifier ou utiliser la valeur de la propriété dans get ou set .

Par exemple:

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

Ensuite:

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
la source
0

La terminologie backing fieldest pleine de mystère. Le mot-clé utilisé est field. Les get/setméthodes suivent immédiatement à côté de la variable membre qui est sur le point d'être obtenue ou définie via ce mécanisme de méthodes de protection de porte. Le fieldmot clé fait simplement référence à la variable membre à définir ou à obtenir . À l'heure actuelle, Kotlin, vous ne pouvez pas faire référence à la variable membre directement dans les méthodes de porte de protection get ou set , car cela entraînera malheureusement une récursion infinie car elle réinvoquera le get ou le set et conduira ainsi le temps d'exécution dans l'abîme profond.

En C # cependant, vous pouvez référencer directement la variable membre dans les méthodes getter / setter. Je cite cette comparaison pour présenter l'idée que ce fieldmot-clé est la façon dont le Kotlin actuel l'implémente, mais j'espère qu'il sera supprimé dans les versions ultérieures et nous permettra de référencer directement la variable membre directement sans entraîner de récursivité infinie.

champ propre
la source