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

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

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

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

person Yudhistira Arya    schedule 05.04.2017    source источник
comment
Использование самого свойства в установщике приведет к бесконечной рекурсии, поскольку присвоение некоторого значения свойству всегда будет вызывать установщик.   -  person funglejunk    schedule 05.04.2017
comment
@Strelok, у меня плохо .... Я предполагал, что this.counter = value совпадает с эквивалентом Java при чтении документации Kotlin.   -  person Yudhistira Arya    schedule 05.04.2017
comment
Эта статья предназначена для поля и свойства.   -  person avinash    schedule 30.04.2018


Ответы (5)


Потому что, скажем, если у вас нет ключевого слова 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 и second Pair. Это свойство изменит представление Pair в памяти.

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

person Community    schedule 05.04.2017
comment
Спасибо за ответ! Мое плохое ... Я предполагал, что this.counter = value то же самое с эквивалентом java. - person Yudhistira Arya; 05.04.2017
comment
Где-нибудь задокументировано? Спасибо:) - person Alston; 27.10.2019
comment
@Alston, вот документация: kotlinlang.org/docs/reference/properties. html # backing-fields - person Yamashiro Rion; 30.12.2019
comment
Много нечетких объяснений. Почему мы не можем просто сказать, что field больше похож на указатель или ссылку на существующую переменную-член. Поскольку get/set сразу следует за counter, ключевое слово field является ссылкой на counter. Верно? - person eigenfield; 13.05.2020
comment
@typelogic этот ответ больше всего подходит для программистов с фоном Java / JS (тогда еще не было Kotlin / Native), а не C / C ++. То, что вы находите нечетким, - это хлеб с маслом для некоторых других людей. - person glee8e; 14.05.2020

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

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

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

Теперь, когда мы смотрим на код, мы видим, что он имеет 2 свойства, а именно - size (с аксессуарами по умолчанию) и isEmpty (с пользовательскими аксессуарами). Но у него всего 1 поле, т.е. size. Чтобы понять, что у него только 1 поле, давайте посмотрим на 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, и для него не объявлено ни одного поля. Точно так же в Kotlin нет поля поддержки для свойства 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.

person thedarkpassenger    schedule 16.01.2018
comment
Хорошее объяснение. Спасибо - person Sonu Sanjeev; 17.10.2019
comment
Право, спасибо за объяснение. Я тоже пришел в Kotlin с Java, и концепция свойств для меня нова. Но я это понял, спасибо вам и экскурсоводам. :) - person Yamashiro Rion; 30.12.2019
comment
Бог может благословить вас. - person Andrea; 27.04.2020
comment
Мне нравится этот ответ - это честно приведенные факты. Я все еще сомневаюсь, потому что C # не нуждается в ключевом слове field, возможно ли, что улучшение языка Kotlin удалит это странное field ключевое слово и не даст беспомощным душам упасть в бездну бесконечной рекурсии ? - person eigenfield; 13.05.2020

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

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

person Mark Mucha    schedule 05.04.2017
comment
Извините, но ваше объяснение содержит термин, который необходимо объяснить. И затем вы сначала процитировали сценарий Java, а затем внезапно без предупреждения переключаете lains на реальный оператор Kotlin. Потребность Kotlin в ключевом слове field отсутствует в C #, поэтому нам нужно объяснение получше, чем то, которое вы здесь процитировали. - person eigenfield; 13.05.2020

Насколько я понимаю, идентификатор поля используется в качестве ссылки на значение свойства в 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)
person Freddie    schedule 31.08.2019

Терминология backing field наполнена тайной. Используемое ключевое слово field. Метод get/set следует сразу за переменной-членом, которая должна быть получена или установлена ​​ с помощью этого механизма методов защиты дверей. Ключевое слово field просто относится к переменной-члену, которую нужно установить или получить. В настоящее время в Kotlin вы не можете ссылаться на переменную-член непосредственно внутри методов защитной двери get или set, потому что это, к сожалению, приведет к бесконечной рекурсии, потому что он повторно вызовет < em> get или set и, таким образом, уводит время выполнения в глубокую бездну.

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

person eigenfield    schedule 13.05.2020