Мне, как 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?
}
}
android
kotlin
kotlin-android-extensions
Юдхиштира Арья
источник
источник
this.counter = value
то же самое с эквивалентом Java, когда читал документы Kotlin.Ответы:
Потому что, скажем, если у вас нет
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
extension ofList
, которое не поддерживается полем, а вычисляется результат на основеsize
свойства. Таким образом, он не изменит представление в памятиList
(чего он вообще не может сделать, потому что Java статически типизирована).источник
this.counter = value
то же самое и с эквивалентом Java.field
больше похоже на указатель или ссылку на существующую переменную-член.get/set
Такимcounter
образом, так как непосредственно следует ,field
ключевое слово является ссылкой наcounter
. Правильно?Поначалу мне тоже было непросто понять эту концепцию. Итак, позвольте мне объяснить вам это на примере.
Рассмотрим этот класс Котлина
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
от него зависят методы получения и установки свойства.источник
field
ключевом слове, возможно ли, что улучшение языка Kotlin удалит это странноеfield
ключевое слово и предотвратит падение беспомощных душ в бездну бесконечной рекурсии?Поля поддержки хороши для запуска проверки или запуска событий при изменении состояния. Вспомните, сколько раз вы добавляли код в средство установки / получения Java. Резервные поля были бы полезны в аналогичных сценариях. Вы могли бы использовать вспомогательные поля, когда вам нужно было контролировать или иметь видимость над сеттерами / получателями.
При присвоении полю самому имени поля вы фактически вызываете сеттер (т.е.
set(value)
). В приведенном у вас примереthis.counter = value
будет рекурсивно переходить в set (value), пока мы не переполним наш стек. Использованиеfield
обходит код установщика (или получателя).источник
field
в Kotlin отсутствует в C #, поэтому нам нужно более подробное объяснение, чем то, которое вы здесь процитировали.Насколько я понимаю, идентификатор поля используется в качестве ссылки на значение свойства в 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)
источник
Терминология
backing field
наполнена тайной. Ключевое слово используетсяfield
. Этиget/set
методы, следует сразу же рядом с переменной - члена , который собирается быть получить или установить через эту дверь механизма защитных методов.field
Ключевое слово просто ссылается на переменную - член , который должен быть установлен или получить . В настоящее время Kotlin вы не можете ссылаться на переменную-член непосредственно внутри методов get или set защитной двери, потому что это, к сожалению, приведет к бесконечной рекурсии, потому что она повторно вызовет get или set и, таким образом, унесет время выполнения в глубокую бездну.Однако в C # вы можете напрямую ссылаться на переменную-член внутри методов получения / установки. Я цитирую это сравнение, чтобы представить идею о том, что это
field
ключевое слово - это то, как текущий Kotlin его реализует, но я надеюсь, что оно будет удалено в более поздних версиях и позволит нам напрямую ссылаться на переменную-член, не приводя к бесконечной рекурсии.источник