Untuk Apa Bidang Dukungan Kotlin?

93

Sebagai pengembang Java, konsep bidang pendukung agak asing bagi saya. Diberikan:

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

Apa gunanya backing field ini? Dokumen Kotlin mengatakan:

Kelas di Kotlin tidak boleh memiliki kolom. Namun, terkadang perlu memiliki backing field saat menggunakan aksesor kustom .

Mengapa? Apa bedanya dengan menggunakan nama properti itu sendiri di dalam penyetel, mis. *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}
Yudhistira Arya
sumber
18
Menggunakan properti itu sendiri di penyetel akan menghasilkan rekursi tanpa akhir karena menetapkan beberapa nilai ke properti akan selalu memanggil penyetel.
funglejunk
1
@Strelok saya buruk .... Saya berasumsi bahwa this.counter = valueitu sama dengan Java yang setara ketika membaca dokumen Kotlin.
Yudhistira Arya
5
Artikel ini ditujukan untuk Bidang vs Properti.
avinash

Jawaban:

86

Karena, katakanlah jika Anda tidak memiliki fieldkata kunci, Anda tidak dapat benar-benar menyetel / mendapatkan nilai di get()atau set(value). Ini memungkinkan Anda untuk mengakses bidang dukungan di pengakses khusus.

Ini adalah kode Java yang setara dari sampel Anda:

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

Rupanya ini tidak baik, karena penyetel hanyalah rekursi infinte ke dalam dirinya sendiri, tidak pernah mengubah apapun. Ingat di kotlin setiap kali Anda menulisnya, foo.bar = valueitu akan diterjemahkan menjadi panggilan penyetel alih-alih PUTFIELD.


EDIT: Java memiliki kolom sementara Kotlin memiliki properti , yang merupakan konsep tingkat yang lebih tinggi daripada kolom.

Ada dua jenis properti: satu dengan bidang belakang, satu tanpa bidang.

Properti dengan backing field akan menyimpan nilai dalam bentuk field. Bidang itu memungkinkan penyimpanan nilai dalam memori. Contoh dari properti tersebut adalah properti firstdan secondproperti Pair. Properti itu akan mengubah representasi dalam memori Pair.

Properti tanpa backing field harus menyimpan nilainya dengan cara lain selain menyimpannya secara langsung di memori. Ini harus dihitung dari properti lain, atau, objek itu sendiri. Contoh dari properti tersebut adalah properti indicesekstensi List, yang tidak didukung oleh bidang, tetapi hasil yang dihitung berdasarkan sizeproperti. Jadi itu tidak akan mengubah representasi dalam memori List(yang tidak dapat dilakukan sama sekali karena Java diketik secara statis).

glee8e
sumber
Terima kasih atas jawabannya! Saya buruk ... Saya berasumsi bahwa this.counter = valueitu sama dengan java setara.
Yudhistira Arya
Dimanapun didokumentasikan? Terima kasih :)
Alston
@Alston, ini dokumentasinya: kotlinlang.org/docs/reference/properties.html#backing-fields
Yamashiro Rion
1
Banyak penjelasan yang kabur. Mengapa kita tidak bisa begitu saja mengatakan, bahwa a fieldlebih seperti pointer atau referensi ke variabel anggota yang sudah ada. Karena itu get/setsegera mengikuti counter, fieldkata kunci adalah referensi ke counter. Baik?
eigenfield
@typelogic jawaban ini paling disesuaikan untuk programmer dengan latar belakang Java / JS (saat itu tidak ada Kotlin / Native), bukan C / C ++. Yang menurut Anda kabur adalah roti dan mentega bagi sebagian orang lain.
glee8e
32

Awalnya, saya juga kesulitan memahami konsep ini. Jadi izinkan saya menjelaskannya kepada Anda dengan bantuan sebuah contoh.

Pertimbangkan kelas Kotlin ini

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

Sekarang ketika kita melihat kode, kita dapat melihat bahwa ia memiliki 2 properti yaitu - size(dengan pengakses default) dan isEmpty(dengan pengakses kustom). Tetapi hanya memiliki 1 bidang yaitu size. Untuk memahami bahwa ia hanya memiliki 1 kolom, mari kita lihat padanan Java untuk kelas ini.

Pergi ke Tools -> Kotlin -> Show Kotlin ByteCode di Android Studio. Klik pada Dekompilasi.

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

Jelas kita dapat melihat bahwa kelas java hanya memiliki fungsi pengambil dan penyetel isEmpty, dan tidak ada bidang yang dideklarasikan untuk itu. Demikian pula di Kotlin, tidak ada isEmptykolom backing untuk properti , karena properti tidak bergantung pada kolom itu sama sekali. Jadi tidak ada backing field.


Sekarang mari kita hapus pengambil dan penyetel isEmptyproperti kustom .

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

Dan padanan Java dari kelas di atas adalah

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

Di sini kita melihat ladang sizedan isEmpty. isEmptyadalah bidang pendukung karena pengambil dan penyetel untuk isEmptyproperti bergantung padanya.

thedarkpassenger
sumber
4
Penjelasan yang bagus. Terima kasih
Sonu Sanjeev
1
Sungguh terima kasih atas penjelasannya. Saya juga datang ke Kotlin dari Java, dan konsep propertinya masih baru bagi saya. Tapi saya sudah memahaminya, terima kasih dan pemandu. :)
Yamashiro Rion
Tuhan memberkati Anda.
Andrea Cioccarelli
Saya suka jawaban ini karena jujur ​​mengutip fakta. Saya masih ragu, karena C # tidak membutuhkan fieldkata kunci, mungkinkah peningkatan bahasa Kotlin akan menghapus fieldkata kunci aneh ini dan menghindari jiwa yang tidak berdaya jatuh ke dalam jurang rekursi tak terbatas?
eigenfield
9

Bidang pendukung baik untuk menjalankan validasi atau memicu peristiwa pada perubahan status. Pikirkan saat-saat Anda menambahkan kode ke penyetel / pengambil Java. Bidang pendukung akan berguna dalam skenario serupa. Anda akan menggunakan backing field saat Anda perlu mengontrol atau memiliki visibilitas atas setter / getter.

Saat menetapkan bidang dengan nama bidang itu sendiri, Anda sebenarnya memanggil penyetel (yaitu set(value)). Dalam contoh yang Anda miliki, this.counter = valueakan berulang menjadi set (nilai) sampai kami meluap tumpukan kami. Menggunakan fieldbypass kode penyetel (atau pengambil).

Mark Mucha
sumber
Maaf, penjelasan Anda mengandung istilah yang perlu dijelaskan. Dan kemudian, Anda pertama kali mengutip skenario Java dan kemudian tiba-tiba tanpa peringatan Anda beralih lain ke pernyataan Kotlin yang sebenarnya. Kotlin membutuhkan kata kunci fieldtidak dalam C # jadi kami membutuhkan penjelasan yang lebih baik daripada yang Anda kutip di sini.
eigenfield
2

Pemahaman saya adalah menggunakan field identifier sebagai referensi nilai properti di get atau set , ketika Anda ingin mengubah atau menggunakan nilai properti di get atau set .

Sebagai contoh:

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

Kemudian:

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
sumber
0

Terminologi backing fielditu penuh dengan misteri. Kata kunci yang digunakan adalah field. The get/setmetode, berikut segera sebelah variabel anggota yang adalah tentang menjadi mendapatkan atau mengatur melalui pintu mekanisme metode pelindung ini. The fieldkata kunci hanya mengacu pada variabel anggota yang akan mengatur atau mendapatkan . Saat ini Kotlin, Anda tidak dapat merujuk ke variabel anggota secara langsung di dalam get atau set metode pintu pelindung karena sayangnya akan menghasilkan rekursi tak terbatas karena itu akan memanggil kembali get atau set dan dengan demikian memimpin runtime ke jurang yang dalam.

Dalam C # , Anda bisa langsung mereferensikan variabel anggota di dalam metode pengambil / penyetel. Saya mengutip perbandingan ini untuk menyajikan gagasan bahwa fieldkata kunci ini adalah bagaimana Kotlin saat ini mengimplementasikannya, tetapi saya berharap ini akan dihapus di versi selanjutnya dan memungkinkan kami untuk merujuk langsung variabel anggota secara langsung tanpa mengakibatkan rekursi tak terbatas.

eigenfield.dll
sumber