Kotlin Backing Field 是干什么的?

作为一名 Java 开发人员,后备字段的概念对我来说有点陌生:

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

这个后场有什么用? Kotlin 医生说:

Kotlin 中的类不能有字段,但是有时在使用自定义访问器 时需要有一个备份字段。

为什么? 在 setter 中使用属性名本身有什么区别,例如 *

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

Because, say if you don't have field keyword, you won't be able to actually set/get the value in the get() or set(value). It enables you to access the backing field in the custom accessors.

这是与示例相同的 Java 代码:

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

显然这样不好,因为 setter 只是自身的无限递归,永远不会改变任何东西。请记住,在 kotlin 中,每当您编写 foo.bar = value时,它将被转换为 setter 调用,而不是 PUTFIELD


编辑: Java 有 田野,而 Kotlin 有 物业,这是一个比字段更高级的概念。

有两种类型的属性: 一种带有后备字段,一种没有。

带有后备字段的属性将以字段的形式存储值。该字段使在内存中存储值成为可能。这种属性的一个例子是 Pairfirstsecond属性。该属性将更改 Pair的内存表示形式。

A property without a backing field will have to store their value in other ways than directly storing it in memory. It must be computed from other properties, or, the object itself. An example of such property is the indices extension property of List, which is not backed by a field, but a computed result based on size property. So it won't change the in-memory representation of List (which it can't do at all because Java is statically typed).

后备字段有助于在状态更改时运行验证或触发事件。想想您向 Java setter/getter 添加代码的次数。在类似的场景中,备份字段将非常有用。当需要控制 setter/getter 或者对它们具有可见性时,可以使用备份字段。

当使用字段名本身分配字段时,实际上是在调用 setter (即 set(value))。在您的示例中,this.counter = value将递归为 set (value) ,直到溢出堆栈为止。使用 field绕过 setter (或 getter)代码。

最初,我也很难理解这个概念。让我用一个例子来解释一下。

Consider this Kotlin class

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

现在,当我们查看代码时,我们可以看到它有2个属性,即 e-size(使用默认访问器)和 isEmpty(使用自定义访问器)。但它只有一个字段,即 size。为了理解它只有1个字段,让我们看看与这个类等价的 Java。

转到 Tools-> Kotlin-> Show Kotlin ByteCode in Android Studio。

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

很明显,我们可以看到 Java 类只有针对 isEmpty的 getter 和 setter 函数,并且没有为它声明任何字段。同样,在 Kotlin,房地产 isEmpty没有后台字段,因为该房地产根本不依赖于该字段。因此没有后备磁场。


现在让我们删除 isEmpty属性的自定义 getter 和 setter。

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

上面类的 Java 等价物是

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

这里我们同时看到字段 sizeisEmptyisEmpty是一个后备字段,因为 isEmpty属性的 getter 和 setter 依赖于它。

My understanding is using 场地 identifier as a reference to the property's value in 走开 or 准备好了, when you want to change or use the property's value in 走开 or 准备好了.

例如:

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

然后:

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)

The terminology backing field is filled with mystery. The keyword used is field. The get/set methods, follows immediately next to the member variable that is about to be 走开 or 准备好了 through this door protective methods mechanism. The field keyword just refers to the member variable that is to be 准备好了 or 走开. At present Kotlin, you cannot refer to the member variable directly inside the 走开 or 准备好了 protective door methods because it will unfortunately result to infinite recursion because it will re-invoke the 走开 or 准备好了 and thus leds the runtime down into the deep abyss.

但是,在 C # 中,您可以直接引用 getter/setter 方法中的成员变量。我引用这个比较是为了说明目前 Kotlin 是如何实现这个 field关键字的,但是我希望它能在以后的版本中被删除,并且允许我们直接引用成员变量,而不会导致无限递归。