Do czego służy zaplecze Kotlin?

93

Jako programista Java koncepcja pola zapasowego jest mi trochę obca. Dany:

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

Do czego służy to pole zapasowe? Kotlin docs powiedział:

Zajęcia w Kotlinie nie mogą mieć pól. Czasami jednak konieczne jest posiadanie pola zapasowego podczas korzystania z niestandardowych akcesorów .

Czemu? Jaka jest różnica w używaniu samej nazwy właściwości wewnątrz settera, np. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
źródło
18
Użycie samej właściwości w metodzie ustawiającym spowoduje nieskończoną rekursję, ponieważ przypisanie pewnej wartości do właściwości zawsze wywoła metodę ustawiającą.
funglejunk
1
@Strelok mój błąd .... Zakładałem, że this.counter = valuetak samo jest z odpowiednikiem Java podczas czytania dokumentacji Kotlina.
Yudhistira Arya
4
Ten artykuł dotyczy Field vs Property.
avinash

Odpowiedzi:

86

Ponieważ, powiedzmy, że nie masz fieldsłowa kluczowego, nie będziesz w stanie ustawić / pobrać wartości w get()lub set(value). Umożliwia dostęp do pola zapasowego w niestandardowych akcesoriach.

Oto odpowiednik kodu Java Twojej próbki:

class Sample {
    private int counter = 0;
    public void setCounter(int value) {
        if (value >= 0) setCounter(value);
    }
    public int getCounter() {
        return counter;
    }
}

Najwyraźniej nie jest to dobre, ponieważ seter jest po prostu nieskończoną rekurencją w sobie, niczego nie zmieniając. Pamiętaj, że w kotlin za każdym razem, gdy napiszesz foo.bar = value, zostanie to przetłumaczone na wywołanie ustawiające zamiast PUTFIELD.


EDYCJA: Java ma pola, a Kotlin ma właściwości , co jest pojęciem wyższego poziomu niż pola.

Istnieją dwa typy właściwości: jedna z polem zapasowym, druga bez.

Właściwość z polem zapasowym będzie przechowywać wartość w postaci pola. To pole umożliwia przechowywanie wartości w pamięci. Przykładem takiej właściwości są właściwości firsti . Ta właściwość zmieni reprezentację w pamięci .secondPairPair

Właściwość bez pola zapasowego będzie musiała przechowywać swoją wartość w inny sposób niż bezpośrednie przechowywanie jej w pamięci. Musi być obliczona na podstawie innych właściwości lub samego obiektu. Przykładem takiej właściwości jest właściwość indicesrozszerzenia List, która nie jest obsługiwana przez pole, ale wynik obliczony na podstawie sizewłaściwości. Więc nie zmieni reprezentacji w pamięci List(czego nie może w ogóle zrobić, ponieważ Java jest wpisywana statycznie).

radość
źródło
Dziękuję za odpowiedź! Mój błąd ... Zakładałem, że this.counter = valuetak samo jest z odpowiednikiem w Javie.
Yudhistira Arya
Gdziekolwiek udokumentowane? Dzięki :)
Alston
@Alston, oto dokumentacja: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Wiele niejasnych wyjaśnień. Dlaczego nie możemy po prostu powiedzieć, że a fieldjest bardziej jak wskaźnik lub odniesienie do istniejącej zmiennej składowej. Ponieważ następuje get/setbezpośrednio po nim counter, fieldsłowo kluczowe odnosi się do counter. Dobrze?
eigenfield
@typelogic ta odpowiedź jest najbardziej dostosowana dla programistów z tłem Java / JS (wtedy nie było Kotlin / Native), a nie C / C ++. To, co uważasz za puszyste, to chleb i masło dla innych osób.
glee8e
31

Na początku też ciężko było mi zrozumieć tę koncepcję. Pozwól, że wyjaśnię ci to na przykładzie.

Rozważ tę klasę Kotlin

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

Teraz, gdy spojrzymy na kod, widzimy, że ma 2 właściwości, tj. - size(z domyślnymi akcesoriami) i isEmpty(z niestandardowymi akcesoriami). Ale ma tylko 1 pole tj size. Aby zrozumieć, że ma tylko 1 pole, zobaczmy odpowiednik tej klasy w Javie.

Idź do Narzędzia -> Kotlin -> Pokaż Kotlin ByteCode w Android Studio. Kliknij Decompile.

   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;
   }
}

Wyraźnie widać, że klasa java ma tylko funkcje pobierające i ustawiające dla isEmptyi nie ma zadeklarowanego dla niej pola. Podobnie w Kotlinie nie ma pola zapasowego dla właściwości isEmpty, ponieważ właściwość w ogóle nie zależy od tego pola. Zatem nie ma pola zapasowego.


Teraz usuńmy niestandardowy pobierający i ustawiający isEmptywłaściwość.

class DummyClass {
    var size = 0;
    var isEmpty = false
}

A odpowiednikiem Java powyższej klasy jest

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;
   }
}

Tutaj widzimy zarówno pola, jak sizei isEmpty. isEmptyjest polem zapasowym, ponieważ metoda pobierająca i ustawiająca isEmptywłaściwość zależy od niego.

thedarkpassenger
źródło
4
Dobre wytłumaczenie. Dziękuję
Sonu Sanjeev
1
Naprawdę dziękuję za wyjaśnienie. Przyjechałem do Kotlina również z Javy i pojęcie właściwości jest dla mnie nowe. Ale zrozumiałem to dzięki tobie i przewodnikom. :)
Yamashiro Rion
Bóg może cię błogosławić.
Andrea Cioccarelli
Podoba mi się ta odpowiedź, szczerze przytoczono fakty. Nadal mam wątpliwości, ponieważ C # nie potrzebuje fieldsłowa kluczowego, czy jest możliwe, że ulepszenie języka Kotlin usunie to dziwne fieldsłowo kluczowe i uniknie bezbronnych dusz wpadających w otchłań nieskończonej rekurencji?
eigenfield
9

Pola zapasowe są dobre do przeprowadzania walidacji lub wyzwalania zdarzeń w przypadku zmiany stanu. Pomyśl o czasach, kiedy dodawałeś kod do settera / gettera Java. Pola zapasowe byłyby przydatne w podobnych scenariuszach. Używałbyś pól zapasowych, gdy trzeba kontrolować lub mieć wgląd w ustawiające / pobierające.

Kiedy przypisujesz pole z samą nazwą pola, w rzeczywistości wywołujesz metodę ustawiającą (tj set(value).). W przykładzie, który masz, this.counter = valuebędzie powtarzał się w set (wartość), dopóki nie przepełnimy naszego stosu. Użycie fieldomija kod ustawiający (lub pobierający).

Mark Mucha
źródło
Przepraszamy, ale Twoje wyjaśnienie zawiera termin, który należy wyjaśnić. A potem najpierw zacytowałeś scenariusz Java, a potem nagle, bez ostrzeżenia, przełączasz się na rzeczywistą instrukcję Kotlin. Kotlin nie potrzebuje słowa kluczowego fieldw języku C #, więc potrzebujemy lepszego wyjaśnienia niż to, które tu przytoczyłeś.
eigenfield
2

Rozumiem, że identyfikator pola jest używany jako odniesienie do wartości właściwości w get lub set , gdy chcesz zmienić lub użyć wartości właściwości w get lub set .

Na przykład:

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}
}

Następnie:

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)
Freddie
źródło
0

Terminologia backing fieldjest pełna tajemnic. Użyte słowo kluczowe to field. Te get/setmetody, wynika bezpośrednio obok zmiennej składowej, która ma być uzyskać lub ustawić za pośrednictwem tej metody drzwi ochronne mechanizmu. Słowo fieldkluczowe po prostu odnosi się do zmiennej składowej, która ma zostać ustawiona lub pobrana . Obecnie Kotlin nie można odwoływać się do zmiennej składowej bezpośrednio w metodach get lub set ochronnych drzwi, ponieważ niestety spowoduje to nieskończoną rekurencję, ponieważ ponownie wywoła get lub set, a tym samym doprowadzi środowisko wykonawcze w głęboką przepaść.

Jednak w C # można bezpośrednio odwoływać się do zmiennej składowej wewnątrz metod pobierających / ustawiających. Cytuję to porównanie, aby przedstawić pomysł, że to fieldsłowo kluczowe jest sposobem implementacji obecnego Kotlina, ale mam nadzieję, że zostanie ono usunięte w późniejszych wersjach i pozwoli nam bezpośrednio odwoływać się do zmiennej składowej bez powodowania nieskończonej rekurencji.

własne
źródło