108

作为一名 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?
    }
}
4

5 回答 5

96

因为,假设您没有field关键字,您将无法实际设置/获取get()or中的值set(value)。它使您能够访问自定义访问器中的支持字段。

这是您的示例的等效 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 有properties,这是一个比字段更高级别的概念。

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

具有支持字段的属性将以字段的形式存储值。该字段使将值存储在内存中成为可能。这种属性的一个例子是 的first和的second属性Pair。该属性将改变Pair.

没有支持字段的属性必须以其他方式存储其值,而不是直接将其存储在内存中。它必须从其他属性或对象本身计算。此类属性的一个示例是 的indices扩展属性List,它不是由字段支持的,而是基于size属性的计算结果。所以它不会改变内存中的表示List(它根本不能这样做,因为 Java 是静态类型的)。

于 2017-04-05T01:28:39.000 回答
40

最初,我也很难理解这个概念。因此,让我借助一个示例向您解释一下。

考虑这个 Kotlin 类

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

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

转到工具 -> Kotlin -> 在 Android Studio 中显示 Kotlin 字节码。点击反编译。

   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 类只有 getter 和 setter 函数isEmpty,并且没有为它声明字段。同样,在 Kotlin 中,property 没有支持字段isEmpty,因为该属性根本不依赖于该字段。因此没有支持领域。


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

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 依赖于它。

于 2018-01-16T06:17:36.033 回答
11

支持字段适用于运行验证或在状态更改时触发事件。想想您向 Java setter/getter 添加代码的时间。支持字段在类似情况下会很有用。当您需要控制或了解 setter/getter 时,您将使用支持字段。

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

于 2017-04-05T01:30:11.413 回答
2

我的理解是当您想更改或使用getset中的属性值时,使用字段标识符作为对getset中属性值的引用。

例如:

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)
于 2019-08-31T15:08:47.643 回答
1

术语backing field充满了神秘色彩。使用的关键字是field. 这些get/set方法紧跟在即将通过此门保护方法机制获取设置的成员变量旁边。field关键字只是指要设置获取的成员变量。目前 Kotlin,你不能直接在getset保护门方法中引用成员变量,因为它会不幸地导致无限递归,因为它会重新调用getset从而将运行时带入深渊。

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

于 2020-05-13T06:52:01.800 回答