【问题标题】:What's Kotlin Backing Field For?Kotlin 支持字段有什么用?
【发布时间】:2017-08-30 09:58:42
【问题描述】:

作为一名 Java 开发人员,支持字段的概念对我来说有点陌生。给定:

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

这个支持字段有什么用? Kotlin docs 说:

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

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

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

【问题讨论】:

  • 在setter中使用属性本身会导致无限递归,因为给属性赋值总是会调用setter。
  • @Strelok 我的错......我在阅读 Kotlin 的文档时假设 this.counter = value 与 Java 等效项相同。
  • article 用于字段与属性。

标签: android kotlin kotlin-android-extensions


【解决方案1】:

因为,假设您没有field 关键字,您将无法实际设置/获取get()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 有 fields,而 Kotlin 有 properties,这是一个比字段更高级别的概念。

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

具有支持字段的属性将以字段的形式存储值。该字段使将值存储在内存中成为可能。此类属性的一个示例是Pairfirstsecond 属性。该属性将更改 Pair 的内存表示。

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

【讨论】:

  • 感谢您的回答!我的错...我假设 this.counter = value 与 java 等效项相同。
  • 任何地方都有记录?谢谢:)
  • 很多模糊的解释。为什么我们不能简单地说,field 更像是一个指针或对现有成员变量的引用。由于get/set 紧跟counter,因此field 关键字是对counter 的引用。对吗?
  • @typelogic 这个答案最适合具有 Java/JS 背景的程序员(当时没有 Kotlin/Native),而不是 C/C++。你觉得模糊的是其他人的面包和黄油。
【解决方案2】:

最初,我也很难理解这个概念。所以让我通过一个例子来给你解释一下。

考虑这个 Kotlin 类

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

现在,当我们查看代码时,我们可以看到它有 2 个属性,即 - size(带有默认访问器)和 isEmpty(带有自定义访问器)。但它只有一个字段,即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 类只有 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;
   }
}

在这里,我们看到了 sizeisEmpty 这两个字段。 isEmpty 是一个支持字段,因为 isEmpty 属性的 getter 和 setter 依赖于它。

【讨论】:

  • 很好的解释。谢谢
  • 真的,谢谢你的解释。我也是从 Java 来到 Kotlin 的,properties 的概念对我来说是新的。但是我已经理解了,感谢您和指南。 :)
  • 上帝会保佑你的。
  • 我喜欢这个答案,它诚实地引用了事实。我还是有疑问,因为C#不需要field关键字,Kotlin的语言改进是否有可能去除这个奇怪的field关键字,避免无助的灵魂陷入无限递归的深渊?
【解决方案3】:

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

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

【讨论】:

  • 抱歉,您的解释中包含需要解释的术语。然后,您首先引用了一个 Java 场景,然后突然毫无预警地切换到一个实际的 Kotlin 语句。关键字 field 的 Kotlin 需求不在 C# 中,因此我们需要比您在此处引用的更好的解释。
【解决方案4】:

我的理解是,当您想更改或使用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)

【讨论】:

    【解决方案5】:

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-17
      • 1970-01-01
      • 2019-12-21
      • 2011-03-21
      • 2020-01-23
      相关资源
      最近更新 更多