Properti adalah variabel yang melekat pada kelas dan dibaca atau ditulis melalui tanda titik, misalnya bidang.sisi. Di balik akses tersebut, Kotlin menggerakkan fungsi akses bernama getter untuk pembacaan dan setter untuk penulisan. Topik ini membahas cara menulis getter dan setter kustom, peran backing field sebagai tempat penyimpanan nilai, serta properti komputasi yang nilainya selalu dihitung dari data lain. Seluruh contoh dijalankan nyata dan keluarannya ditampilkan sebagaimana adanya.
Properti pada kelas#
Properti dideklarasikan di dalam tubuh kelas dengan val untuk nilai yang tetap atau var untuk nilai yang boleh berubah, dan bentuk paling ringkasnya berada langsung pada parameter constructor utama. Akses dari luar kelas memakai tanda titik tanpa pemanggilan fungsi apa pun, walaupun di dalamnya tetap ada getter dan setter yang bekerja. Properti yang dideklarasikan dengan val hanya menyediakan getter, sehingga usaha menulis ulang nilainya ditolak compiler.
Buat berkas main.kt berikut, lalu jalankan dengan dua perintah di bawahnya.
class PersegiPanjang(sisi: Double) {
var sisi: Double = sisi
set(value) {
field = if (value > 0.0) value else field
}
val luas: Double
get() = sisi * sisi
val keliling: Double
get() = 4 * sisi
}
class Suhu(var celsius: Double) {
val fahrenheit: Double
get() = celsius * 9 / 5 + 32
}
fun main() {
val bidang = PersegiPanjang(4.0)
println("Sisi: ${bidang.sisi}, luas: ${bidang.luas}, keliling: ${bidang.keliling}")
bidang.sisi = -2.0
println("Sisi setelah nilai negatif: ${bidang.sisi}, luas: ${bidang.luas}")
bidang.sisi = 6.0
println("Sisi baru: ${bidang.sisi}, luas: ${bidang.luas}")
val suhu = Suhu(30.0)
println("30 derajat Celsius sama dengan ${suhu.fahrenheit} derajat Fahrenheit")
suhu.celsius = 25.0
println("25 derajat Celsius sama dengan ${suhu.fahrenheit} derajat Fahrenheit")
}# compiler Kotlin dan pustaka standar yang dipakai jalur ini
export PATH=/home/kal/my-project/tools/kotlinc/bin:/home/kal/my-project/tools/jdk-merged/bin:$PATH
export STDLIB=/home/kal/my-project/tools/kotlinc/lib/kotlin-stdlib.jar
# kompilasi berkas main.kt menjadi out.jar, lalu jalankan programnya
kotlinc main.kt -d out.jar && java -cp "out.jar:$STDLIB" MainKtSisi: 4.0, luas: 16.0, keliling: 16.0
Sisi setelah nilai negatif: 4.0, luas: 16.0
Sisi baru: 6.0, luas: 36.0
30 derajat Celsius sama dengan 86.0 derajat Fahrenheit
25 derajat Celsius sama dengan 77.0 derajat FahrenheitKeluaran di atas memperlihatkan tiga perilaku berbeda pada satu program. Penulisan bidang.sisi = -2.0 tidak mengubah nilai apa pun karena setter menolak nilai yang tidak positif, sehingga sisi tetap 4.0 dan luasnya tetap 16.0. Penulisan bidang.sisi = 6.0 diterima, dan luas yang dibaca sesaat setelahnya langsung mengikuti nilai baru tanpa pemanggilan fungsi peremajaan apa pun. Perilaku ketiga terlihat pada Suhu, yang properti fahrenheit-nya selalu dihitung ulang dari celsius terkini.
Getter dan setter kustom#
Getter kustom ditulis dengan get() di bawah deklarasi properti, sedangkan setter kustom ditulis dengan set(value). Keduanya boleh berbentuk ekspresi tunggal seperti get() = sisi * sisi atau berbentuk blok lengkap dengan kurung kurawal. Setter selalu menerima satu parameter yang membawa nilai baru, dan kebiasaan penamaan yang dipakai di jalur ini adalah value. Bentuk ekspresi tunggal dipilih untuk perhitungan pendek, sedangkan bentuk blok dipilih ketika logikanya memerlukan beberapa pernyataan.
Setter pada sisi memperlihatkan pola penjagaan nilai, yaitu memeriksa value sebelum menyimpannya. Pola ini menjaga agar setiap instans PersegiPanjang tidak pernah menyimpan sisi negatif atau nol. Perlu dicatat bahwa penolakan semacam ini berjalan senyap, karena nilai yang tidak valid dibuang tanpa pesan apa pun.
Backing field#
Backing field adalah tempat penyimpanan internal yang menampung nilai sebuah properti bertipe var atau val berinisialisator. Identifier khusus field hanya dapat dipakai di dalam getter dan setter untuk membaca atau mengisi penyimpanan itu, dan inilah yang membuat setter sisi dapat menolak nilai baru tanpa membuat perulangan tak berujung. Apabila setter ditulis sisi = value alih-alih field = value, setter akan memanggil dirinya sendiri berulang kali sampai program berhenti dengan stack overflow.
Aturan tersebut menjelaskan mengapa properti komputasi tidak membebani instans. Compiler yang menemukan akses field pada accessor akan menyediakan penyimpanannya secara otomatis, sedangkan accessor yang tidak menyentuh field membiarkan properti berperilaku seperti fungsi biasa.
Properti komputasi#
Properti komputasi adalah properti tanpa penyimpanan yang nilainya selalu diperoleh dari data lain pada saat dibaca. luas dihitung dari sisi, keliling dihitung dari sisi, dan fahrenheit dihitung dari celsius, sehingga tidak ada kemungkinan nilainya kedaluwarsa. Properti semacam ini dipilih ketika nilai benar-benar merupakan turunan langsung dari data lain, sedangkan properti berpenyimpanan dipilih ketika nilai diperoleh dari luar kelas dan perlu diingat.
Perbedaannya dengan fungsi terletak pada pembacaan, bukan pada kemampuan. Properti menjanjikan akses yang murah dan tanpa efek samping, sehingga perhitungan yang berat sebaiknya tidak disembunyikan di dalam getter. Apabila membaca satu properti menuntut perhitungan mahal atau memicu perubahan keadaan, fungsi dengan nama yang menjelaskan pekerjaannya lebih jujur dipakai.
Kesalahan umum#
Kesalahan yang sering terjadi adalah menulis ulang properti val, misalnya berusaha mengisi luas secara langsung. Berkas berikut sengaja memuat kesalahan tersebut.
class Persegi(val sisi: Double) {
val luas: Double
get() = sisi * sisi
}
fun main() {
val bidang = Persegi(4.0)
bidang.luas = 16.0
println(bidang.luas)
}# kompilasi berkas salahluas.kt untuk melihat pesan error dari compiler
kotlinc salahluas.kt -d salah.jarsalahluas.kt:8:12: error: 'val' cannot be reassigned.
bidang.luas = 16.0
^^^^Pesan 'val' cannot be reassigned muncul karena properti val tidak menyediakan setter, sehingga penulisan ulang tidak mungkin dilakukan. Perbaikannya bergantung pada maksudnya, yaitu mengubah sisi melalui properti yang memang dirancang untuk ditulis, atau mendeklarasikan properti tersebut sebagai var apabila nilai memang dimaksudkan untuk diganti dari luar. Pada contoh ini, mengisi sisi adalah jalur yang benar karena luas diturunkan dari sisi.
Ringkasan#
- Properti kelas dibaca dan ditulis melalui tanda titik, dengan getter dan setter yang bekerja di belakangnya.
- Getter dan setter kustom ditulis dengan
get()danset(value), dan setter menerima nilai baru melalui parametervalue. - Backing field diakses dengan identifier
fielddi dalam accessor dan hanya dibuat apabila accessor memakainya. - Properti komputasi tanpa penyimpanan menurunkan nilainya dari data lain pada setiap pembacaan, sehingga tidak pernah kedaluwarsa.
Referensi#
- Properties — Kotlin Documentation — Dokumentasi resmi deklarasi properti, getter dan setter, serta backing field.