什么是Kotlin支持领域?

时间:2017-04-05 01:02:18

标签: android kotlin kotlin-android-extensions

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

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">

    </activity>
    <activity android:name=".Main2Activity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />

        </intent-filter>
    </activity>
</application>

这个支持领域有什么用? Kotlin docs说: Kotlin中的类不能有字段。但是,有时在使用自定义访问器时需要有一个支持字段。为什么?在setter中使用属性名称本身的区别是什么。

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

5 个答案:

答案 0 :(得分:61)

因为,如果您没有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只是对自身的infinte递归,从不改变任何东西。每当你写foo.bar = value时,请记住在kotlin中它将被翻译成一个setter调用,而不是PUTFIELD

编辑:Java有字段,而Kotlin有属性,这是一个比字段更高级别的概念。

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

具有支持字段的属性将以字段的形式存储值。该字段使存储值可以存储在内存中。此类属性的一个示例是first的{​​{1}}和second属性。该属性将更改Pair的内存中表示。

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

答案 1 :(得分:10)

最初,我也很难理解这个概念。所以,让我借助一个例子向你解释。

考虑这个Kotlin类

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

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

转到工具 - &gt; Kotlin - &gt;在Android Studio中显示Kotlin ByteCode。单击“反编译”。

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

答案 2 :(得分:9)

备份字段适用于运行验证或触发状态更改事件。想想你为Java setter / getter添加代码的时间。在类似的场景中,支持字段会很有用。当您需要控制或了解setter / getter时,您可以使用支持字段。

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

答案 3 :(得分:0)

我的理解是,要更改或使用字段标识符作为获取设置中属性值的参考获取设置中的属性值。

例如:

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)

答案 4 :(得分:0)

术语backing field充满神秘色彩。使用的关键字是field。通过该门保护方法机制,get/set方法紧随将要成为 get set 的成员变量之后。 field关键字仅指要进行 set get 的成员变量。目前,Kotlin不能直接在 get set 防护门方法内引用成员变量,因为不幸的是,它将导致无限递归,因为它将重新调用< em> get 或 set ,从而导致运行时下降到深渊。

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