Для чого потрібне поле Котліна?


93

Як розробнику Java, концепція поля підтримки є для мене дещо чужою. Дано:

class Sample {
    var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value
    }
}

Для чого корисне це поле підтримки? Котлін Докс сказав:

Класи в Котліні не можуть мати полів. Однак іноді під час користувальницьких аксесуарів необхідно мати поле для підтримки .

Чому? Яка різниця з використанням самого імені властивостей всередині сеттера, наприклад. *

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

18
Використання самого властивості в сеттері призведе до нескінченної рекурсії, оскільки присвоєння деякого значення властивості завжди буде викликати сетер.
funglejunk

1
@Strelok мій поганий .... Я припускав, що this.counter = valueте саме саме з еквівалентом Java, читаючи документи Котліна.
Yudhistira Arya

5
Ця стаття призначена для поля проти власності.
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.


EDIT: Java має поля, тоді як Kotlin має властивості , що є поняттям набагато вищого рівня, ніж поля.

Існує два типи властивостей: одне з полем підкладки, одне без.

Властивість із заповнювальним полем буде зберігати значення у вигляді поля. Це поле робить можливим зберігання значення в пам'яті. Прикладом такої властивості є firstі secondвластивості Pair. Ця властивість змінить представлення в пам'яті Pair.

Властивість без поля заповнення повинна зберігати своє значення іншими способами, ніж безпосередньо зберігати його в пам'яті. Він повинен бути обчислений з інших властивостей, або самого об'єкта. Прикладом такої властивості є властивість indicesрозширення List, яка не підтримується полем, а обчислюваним результатом на основі sizeвластивості. Отже, це не змінить представлення в пам'яті List(що взагалі не вдається зробити, оскільки Java статично набрана).


Дякую за відповідь! Моя погана ... Я припускав, що this.counter = valueте саме саме з еквівалентом Java.
Yudhistira Arya

Десь задокументовано? Дякую :)
Алстон,


1
Багато нечітких пояснень. Чому ми не можемо просто сказати, що a fieldбільше нагадує покажчик або посилання на існуючу змінну-член. Оскільки get/setбезпосередньо слід, counterотже, fieldключове слово є посиланням на counter. Правда?
власне поле

@typelogic ця відповідь найбільш пристосована для програмістів із фонами Java / JS (тоді ще не було Kotlin / Native), а не C / C ++. Що ви вважаєте нечітким - це хліб та масло для деяких інших людей.
glee8e

32

Спочатку мені теж було важко зрозуміти цю концепцію. Тож дозвольте пояснити вам це на прикладі.

Розглянемо цей клас Котліна

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 є лише функції getter та setter для 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
Дійсно, дякую за пояснення. Я також приїхав до Котліна з Java, і концепція властивостей для мене нова. Але я це зрозумів, завдяки вам і путівникам. :)
Yamashiro Rion

Бог може вас благословити.
Андреа Чоккареллі,

Мені подобається ця відповідь, в ній чесно цитуються факти. Я все ще сумніваюся, тому що C # не потребує fieldключових слів, чи можливо, що вдосконалення мови Kotlin видалить це дивне fieldключове слово і уникне безпорадних душ від падіння в прірву нескінченної рекурсії?
власне поле

9

Поля для резервного копіювання корисні для запуску перевірки або активації подій при зміні стану. Подумайте, коли ви додавали код до Java-установника / отримувача. Поля резервного копіювання були б корисними в подібних сценаріях. Ви б використовували резервні поля, коли вам потрібно було контролювати або мати видимість над сетерами / геттерами.

Призначаючи поле самому імені поля, ви фактично викликаєте сетер (тобто set(value)). У прикладі, який ви маєте, this.counter = valueбуде повертатися в set (value), поки ми не переповнимо наш стек. Використання fieldобходу коду сетера (або геттера).


Вибачте, але ваше пояснення містить термін, який потрібно пояснити. А потім ви спочатку процитували сценарій Java, а потім раптом без попередження переключите lains на фактичну заяву Kotlin. Потреба Котліна в ключовому fieldслові не в C #, тому нам потрібне краще пояснення, ніж те, яке ви цитували тут.
власне поле

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Ключове слово просто посилається на змінну - член , який повинен бути встановлений або отримати . В даний час Котлін, ви не можете посилатися на змінну-учасника безпосередньо всередині методів захисних дверей get або set, оскільки це, на жаль, призведе до нескінченної рекурсії, оскільки вона повторно викликає get або set і, таким чином, виводить час роботи в глибоку безодню.

Однак у C # ви можете безпосередньо посилатися на змінну-член всередині методів getter / setter. Я посилаюся на це порівняння, щоб представити ідею, що це fieldключове слово - це те, як нинішній Котлін реалізує його, але я сподіваюся, що воно буде видалено в пізніших версіях і дозволить нам безпосередньо посилатися на змінну-член, не приводячи до нескінченної рекурсії.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.