Подтвердить что ты не робот

Что такое поле для резервного копирования 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?
        }
    }
4b9b3361

Ответ 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)