¿Para qué sirve Kotlin Backing Field?

92

Como desarrollador de Java, el concepto de campo de respaldo me resulta un poco extraño. Dado:

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

¿Para qué sirve este campo de apoyo? Los documentos de Kotlin dijeron:

Las clases en Kotlin no pueden tener campos. Sin embargo, a veces es necesario tener un campo de respaldo cuando se utilizan descriptores de acceso personalizados .

¿Por qué? ¿Cuál es la diferencia con el uso del nombre de las propiedades dentro del setter, por ejemplo, *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
fuente
17
El uso de la propiedad en sí en el establecedor dará como resultado una recursividad sin fin, ya que la asignación de algún valor a la propiedad siempre llamará al establecedor.
funglejunk
1
@Strelok mi mal ... Supuse que this.counter = valuees lo mismo con el equivalente de Java al leer los documentos de Kotlin.
Yudhistira Arya
4
Este artículo es para campo vs propiedad.
avinash

Respuestas:

86

Porque, digamos que si no tiene una fieldpalabra clave, no podrá establecer / obtener el valor en get()o set(value). Le permite acceder al campo de respaldo en los descriptores de acceso personalizados.

Este es el código Java equivalente de su muestra:

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

Aparentemente, esto no es bueno, ya que el colocador es solo una recursividad infinita en sí mismo, sin cambiar nada. Recuerde que en kotlin cada vez que escriba foo.bar = valuese traducirá en una llamada de setter en lugar de a PUTFIELD.


EDITAR: Java tiene campos mientras que Kotlin tiene propiedades , que es un concepto de nivel bastante más alto que los campos.

Hay dos tipos de propiedades: una con un campo de respaldo y otra sin él.

Una propiedad con un campo de respaldo almacenará el valor en forma de campo. Ese campo hace posible almacenar valor en la memoria. Un ejemplo de dicha propiedad son las propiedades firsty secondde Pair. Esa propiedad cambiará la representación en memoria de Pair.

Una propiedad sin un campo de respaldo tendrá que almacenar su valor de otra forma que no sea almacenarlo directamente en la memoria. Debe calcularse a partir de otras propiedades o del propio objeto. Un ejemplo de dicha propiedad es la indicespropiedad de extensión de List, que no está respaldada por un campo, sino un resultado calculado basado en la sizepropiedad. Por lo tanto, no cambiará la representación en memoria de List(lo que no puede hacer en absoluto porque Java está tipado estáticamente).

glee8e
fuente
¡Gracias por la respuesta! Mi mal ... estaba asumiendo que this.counter = valuees lo mismo con el equivalente de Java.
Yudhistira Arya
¿En algún lugar documentado? Gracias :)
Alston
@Alston, aquí está la documentación: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Muchas explicaciones confusas. ¿Por qué no podemos decir simplemente que a fieldes más como un puntero o una referencia a una variable miembro existente? Como get/setsigue inmediatamente counter, la fieldpalabra clave es una referencia a counter. ¿Derecho?
eigenfield
@typelogic, esta respuesta está más diseñada para programadores con antecedentes de Java / JS (en ese entonces no hay Kotlin / Native), no C / C ++. Lo que te parece confuso es pan y mantequilla para otras personas.
glee8e
31

Al principio, a mí también me costó mucho entender este concepto. Déjame explicártelo con la ayuda de un ejemplo.

Considere esta clase de Kotlin

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

Ahora, cuando miramos el código, podemos ver que tiene 2 propiedades, es decir - size(con accesos predeterminados) y isEmpty(con accesos personalizados). Pero solo tiene 1 campo, es decir size. Para entender que solo tiene 1 campo, veamos el equivalente en Java de esta clase.

Vaya a Herramientas -> Kotlin -> Mostrar Kotlin ByteCode en Android Studio. Haga clic en Descompilar.

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

Claramente podemos ver que la clase java solo tiene funciones getter y setter para isEmpty, y no hay ningún campo declarado para ello. De manera similar, en Kotlin, no hay un campo de respaldo para la propiedad isEmpty, ya que la propiedad no depende en absoluto de ese campo. Por lo tanto, no hay campo de respaldo.


Ahora eliminemos el getter personalizado y el setter de isEmptypropiedad.

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

Y el equivalente en Java de la clase anterior es

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

Aquí vemos tanto los campos sizecomo isEmpty. isEmptyes un campo de respaldo porque el captador y el definidor de la isEmptypropiedad dependen de él.

el pasajero oscuro
fuente
4
Buena explicación. Gracias
Sonu Sanjeev
1
De verdad, gracias por la explicación. También vine a Kotlin desde Java, y el concepto de propiedades es nuevo para mí. Pero lo he entendido, gracias a ustedes y guías. :)
Yamashiro Rion
Dios puede bendecirlos.
Andrea Cioccarelli
Me gusta esta respuesta, se citan honestamente los hechos. Todavía tengo dudas, debido a que C # no necesita fieldpalabras clave, ¿es posible que una mejora del lenguaje de Kotlin elimine esta extraña fieldpalabra clave y evite que las almas indefensas caigan en el abismo de la recursividad infinita?
eigenfield
9

Los campos de respaldo son buenos para ejecutar la validación o desencadenar eventos en el cambio de estado. Piense en las veces que ha agregado código a un setter / getter de Java. Los campos de respaldo serían útiles en escenarios similares. Usaría campos de respaldo cuando necesitara controlar o tener visibilidad sobre los setters / getters.

Al asignar el campo con el nombre del campo en sí, en realidad está invocando al establecedor (es decir set(value)). En el ejemplo que tiene, this.counter = valuerecurriría a set (value) hasta que desbordemos nuestra pila. El uso fieldomite el código de establecimiento (o captador).

Mark Mucha
fuente
Lo siento, pero su explicación contiene el término que debe explicarse. Y luego, primero citó un escenario de Java y luego, de repente, sin previo aviso, cambió lains a una declaración real de Kotlin. La necesidad de Kotlin para la palabra clave fieldno está en C #, por lo que necesitamos una mejor explicación que la que citó aquí.
eigenfield
2

Tengo entendido que está usando el identificador de campo como referencia al valor de la propiedad en get o set , cuando desea cambiar o usar el valor de la propiedad en get o set .

Por ejemplo:

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

Entonces:

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
fuente
0

La terminología backing fieldestá llena de misterio. La palabra clave utilizada es field. Los get/setmétodos, siguen inmediatamente al lado de la variable miembro que está a punto de ser obtenida o configurada a través de este mecanismo de métodos de protección de puertas. La fieldpalabra clave solo se refiere a la variable miembro que se va a establecer u obtener . En la actualidad, Kotlin, no puede hacer referencia a la variable miembro directamente dentro de los métodos de puerta protectora get o set porque, desafortunadamente, resultará en una recursividad infinita porque volverá a invocar el get o set y, por lo tanto, llevará el tiempo de ejecución al abismo profundo.

Sin embargo, en C # , puede hacer referencia directamente a la variable miembro dentro de los métodos getter / setter. Estoy citando esta comparación para presentar la idea de que esta fieldpalabra clave es la forma en que Kotlin actual la está implementando, pero espero que se elimine en versiones posteriores y nos permita hacer referencia directamente a la variable miembro sin que resulte en una recursividad infinita.

eigenfield
fuente