Что такое поле для резервного копирования 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 сказал: Классы в Котлине не могут иметь поля. Однако иногда при использовании пользовательских аксессуаров необходимо иметь резервное поле. Зачем? В чем разница с использованием самого имени свойства внутри установщика, например.
class Sample {
var counter = 0
set(value) {
if (value >= 0) this.counter = value // or just counter = value?
}
}
Ответы
Ответ 1
Потому что, скажем, если у вас нет ключевого слова 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 статически типизирован).
Ответ 2
Сначала мне тоже было непросто понять эту концепцию. Итак, позвольте мне объяснить это вам на примере.
Рассмотрим этот класс Kotlin
class DummyClass {
var size = 0;
var isEmpty
get() = size == 0
set(value) {
size = size * 2
}
}
Теперь, когда мы посмотрим на код, мы увидим, что у него есть 2 свойства, т.е. - size
(со стандартными средствами доступа) и isEmpty
(со специальными средствами доступа). Но у него есть только 1 поле, то есть size
. Чтобы понять, что оно имеет только 1 поле, давайте посмотрим Java-эквивалент этого класса.
Перейдите в Инструменты → Kotlin → Показать Kotlin ByteCode в 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
.
Ответ 3
Поддерживаемые поля хороши для запуска проверки или запуска событий при изменении состояния. Подумайте о том, как вы добавили код в Java setter/getter. В подобных сценариях поле будет полезным. Вы должны использовать резервные поля, когда вам нужно контролировать или иметь видимость через сеттеры/получатели.
При назначении поля самому имени поля вы фактически вызываете установщик (т.е. set(value)
). В приведенном примере this.counter = value
будет возвращаться в set (value), пока мы не переполним наш стек. Использование field
обходит код сеттера (или getter).
Ответ 4
Насколько я понимаю, использование поля идентификатора в качестве ссылки на значение свойства в 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)