Kotlin支持场有什么用?


92

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

17
在setter中使用属性本身将导致无限递归,因为为该属性分配一些值将始终调用setter。
funglejunk

1
@Strelok,我的错。。。我假设this.counter = value阅读Kotlin的文档时与Java等效。
Yudhistira Arya

3
文章是用于现场VS物业。
avinash

Answers:


86

因为,例如,如果您没有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;
    }
}

显然这不好,因为二传手只是对自己的无限次递归,永远不会改变任何东西。请记住,在kotlin中,每次编写foo.bar = value时都会将其转换为setter调用,而不是PUTFIELD


编辑:Java有字段,而Kotlin有properties,这是一个比字段更高层次的概念。

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

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

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


谢谢你的回答!我的坏...我以为this.counter = value与Java等效。
Yudhistira Arya

哪里有文件记录?谢谢:)
Alston


1
很多模糊的解释。我们为什么不能简单地说:afield更像是对现有成员变量的指针或引用。由于get/set紧随其后,counter因此field关键字是对的引用counter。对?
艾根菲尔德

@typelogic这个答案最适合具有Java / JS背景的程序员(当时没有Kotlin / Native),而不是C / C ++。您发现模糊的是其他人的面包和黄油。
glee8e

30

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

考虑这个科特林课

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 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类仅具有getter和setter函数isEmpty,并且没有为其声明任何字段。类似地,在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属性的获取器和设置器依赖于它。


4
很好的解释。谢谢
Sonu Sanjeev,

1
真的,谢谢您的解释。我也从Java来到Kotlin,而属性的概念对我来说是新的。但是,我已经理解了,感谢您和向导。:)
Yamashiro Rion

上帝可以保佑你。
Andrea Cioccarelli

我喜欢这个答案,它是诚实地引用事实。我仍然对此表示怀疑,因为C#不需要field关键字,那么Kotlin的语言改进是否有可能消除这个奇怪的field关键字并避免无助的灵魂陷入无限递归的深渊?
艾根菲尔德

9

支持字段适合于运行验证或在状态更改时触发事件。想想您将代码添加到Java setter / getter的时间。在类似情况下,后备字段将很有用。在需要控制设置者/获取者或对其具有可见性时,将使用后备字段。

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


抱歉,您的解释中包含需要解释的术语。然后,您首先引用了Java场景,然后突然在没有警告的情况下将延迟切换为实际的Kotlin语句。Kotlin对关键字field的需求不在C#中,因此我们需要比您在此处引用的内容更好的解释。
艾根菲尔德

2

我的理解是利用标识符的参考属性值获取设定,当你想更改或使用该属性的值获取设置

例如:

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)

0

这个术语backing field充满了神秘色彩。使用的关键字是field。这些get/set方法紧随即将通过此门保护方法机制获取设置的成员变量之后。该field关键字只是指成员变量被设置获取。当前,Kotlin不能直接在getset保护性方法内部引用成员变量,因为不幸的是,由于它将重新调用getset并导致运行时陷入深渊,因此将导致无限递归。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.