Для чего нужна поддержка Kotlin?

93

Мне, как 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 не могут иметь полей. Однако иногда необходимо иметь поле поддержки при использовании настраиваемых средств доступа .

Зачем? В чем разница с использованием самого имени свойства внутри установщика, например. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Юдхиштира Арья
источник
18
Использование самого свойства в установщике приведет к бесконечной рекурсии, поскольку присвоение некоторого значения свойству всегда будет вызывать установщик.
губчатый джанк 05
1
@Strelok, мой плохой .... Я предполагал, что this.counter = valueто же самое с эквивалентом Java, когда читал документы Kotlin.
Юдхистира Арья
4
Эта статья посвящена полю и собственности.
avinash

Ответы:

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он будет переведен в вызов установщика вместо PUTFIELD.


РЕДАКТИРОВАТЬ: в Java есть поля, а в Kotlin - свойства , что является концепцией более высокого уровня, чем поля.

Есть два типа свойств: один с резервным полем, другой без него.

Свойство с резервным полем будет хранить значение в форме поля. Это поле позволяет сохранять значение в памяти. Примером такого свойства являются свойства firstи . Это свойство изменит представление в памяти .secondPairPair

Свойство без резервного поля должно будет хранить свое значение другими способами, кроме непосредственного сохранения его в памяти. Он должен быть вычислен из других свойств или самого объекта. Примером такого свойства является свойство indicesextension of List, которое не поддерживается полем, а вычисляется результат на основе sizeсвойства. Таким образом, он не изменит представление в памяти List(чего он вообще не может сделать, потому что Java статически типизирована).

glee8e
источник
Спасибо за ответ! Моя проблема ... Я предполагал, что this.counter = valueто же самое и с эквивалентом Java.
Юдхистира Арья
Где-нибудь задокументировано? Спасибо :)
Alston
@Alston, вот документация: kotlinlang.org/docs/reference/properties.html#backing-fields
Рион
1
Очень много нечетких объяснений. Почему мы не можем просто сказать, что a fieldбольше похоже на указатель или ссылку на существующую переменную-член. get/setТаким counterобразом, так как непосредственно следует , fieldключевое слово является ссылкой на counter. Правильно?
eigenfield
@typelogic этот ответ больше всего подходит для программистов с фоном Java / JS (тогда еще не было Kotlin / Native), а не C / C ++. То, что вы находите нечетким, - это хлеб с маслом для некоторых других людей.
glee8e
31

Поначалу мне тоже было непросто понять эту концепцию. Итак, позвольте мне объяснить вам это на примере.

Рассмотрим этот класс Котлина

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

Теперь, когда мы смотрим на код, мы видим, что у него есть 2 свойства, а именно - size(со стандартными аксессуарами) и isEmpty(с пользовательскими аксессуарами). Но она имеет только 1 поле ИЭ size. Чтобы понять, что у него только одно поле, давайте посмотрим на Java-эквивалент этого класса.

Перейдите в Инструменты -> Котлин -> Показать байтовый код Котлина в 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, и для него не объявлено поле. Точно так же в Котлине нет поддерживающего поля для свойства isEmpty, поскольку свойство вообще не зависит от этого поля. Таким образом, нет поля поддержки.


Теперь давайте удалим пользовательские методы получения и установки 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;
   }
}

Здесь мы видим как поля, так sizeи isEmpty. isEmptyявляется вспомогательным полем, потому что isEmptyот него зависят методы получения и установки свойства.

темный пассажир
источник
4
Хорошее объяснение. Спасибо
Сону Санджив
1
Право, спасибо за объяснение. Я тоже пришел в Kotlin с Java, и концепция свойств для меня нова. Но я понял это, благодаря вам и гидам. :)
Рион
Бог может благословить вас.
Андреа Чоккарелли,
Мне нравится этот ответ - это честно приведенные факты. Я все еще сомневаюсь, потому что C # не нуждается в fieldключевом слове, возможно ли, что улучшение языка Kotlin удалит это странное fieldключевое слово и предотвратит падение беспомощных душ в бездну бесконечной рекурсии?
eigenfield
9

Поля поддержки хороши для запуска проверки или запуска событий при изменении состояния. Вспомните, сколько раз вы добавляли код в средство установки / получения Java. Резервные поля были бы полезны в аналогичных сценариях. Вы могли бы использовать вспомогательные поля, когда вам нужно было контролировать или иметь видимость над сеттерами / получателями.

При присвоении полю самому имени поля вы фактически вызываете сеттер (т.е. set(value)). В приведенном у вас примере this.counter = valueбудет рекурсивно переходить в set (value), пока мы не переполним наш стек. Использование fieldобходит код установщика (или получателя).

Марк Муха
источник
Извините, но ваше объяснение содержит термин, который необходимо пояснить. И затем вы сначала процитировали сценарий Java, а затем внезапно без предупреждения переключаете lains на фактический оператор Kotlin. Необходимость ключевого слова fieldв Kotlin отсутствует в C #, поэтому нам нужно более подробное объяснение, чем то, которое вы здесь процитировали.
eigenfield
2

Насколько я понимаю, идентификатор поля используется в качестве ссылки на значение свойства в get или set , когда вы хотите изменить или использовать значение свойства в get или set .

Например:

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 вы не можете ссылаться на переменную-член непосредственно внутри методов get или set защитной двери, потому что это, к сожалению, приведет к бесконечной рекурсии, потому что она повторно вызовет get или set и, таким образом, унесет время выполнения в глубокую бездну.

Однако в C # вы можете напрямую ссылаться на переменную-член внутри методов получения / установки. Я цитирую это сравнение, чтобы представить идею о том, что это fieldключевое слово - это то, как текущий Kotlin его реализует, но я надеюсь, что оно будет удалено в более поздних версиях и позволит нам напрямую ссылаться на переменную-член, не приводя к бесконечной рекурсии.

собственное поле
источник