Init block adalah blok kode yang dieksekusi saat sebuah object dibuat, tepat setelah primary constructor selesai menugaskan parameter ke property. Berbeda dengan Java yang menempatkan logika constructor di dalam badan constructor, Kotlin memisahkan deklarasi property dari logika inisialisasi melalui init block. Pada chapter ini kita akan memahami urutan eksekusi init block terhadap property initializer, perilaku multiple init block, serta cara memanfaatkan init block untuk validasi.
Kapan Init Block Dieksekusi #
Saat sebuah object dibuat dengan Produk("Laptop", 1500000), kompilator menjalankan tahapan berurutan berikut:
- Mengalokasikan memori untuk object baru.
- Menugaskan argumen constructor ke parameter
namadanhargaAwal. - Mengeksekusi property initializer dan init block sesuai urutan deklarasi dari atas ke bawah.
- (Bila ada) menjalankan body secondary constructor.
- Mengembalikan referensi object ke pemanggil.
Karena init block berbagi urutan dengan property initializer, posisi deklarasi sangat menentukan kapan kode di dalamnya dijalankan. Sebuah property yang dideklarasikan setelah sebuah init block belum memiliki nilai saat init block tersebut dieksekusi.
Multiple Init Block dalam Urutan Deklarasi #
Kotlin mengizinkan sebuah class memiliki lebih dari satu init block. Semua init block dieksekusi sesuai urutan kemunculan dalam kode sumber, berseling dengan property initializer yang berada di antara mereka. Pola ini sering dipakai untuk mengelompokkan logika inisialisasi yang berbeda secara semantik.
class Contoh(a: Int) {
val x: Int = a + 1
init {
println("Init pertama, x=$x")
}
val y: Int = x * 2
init {
println("Init kedua, y=$y")
}
}Saat object dibuat, urutan eksekusi adalah: x dihitung, init pertama dijalankan, y dihitung, init kedua dijalankan. Inisialisasi tidak pernah melompat-lompat terlepas dari letak deklarasi.
Contoh Lengkap dengan Validasi #
Contoh berikut memperlihatkan class Produk dengan tiga init block yang berbagi tugas: pencatatan, perhitungan property, dan validasi. Property diskon dideklarasikan di antara init block untuk membuktikan urutan eksekusi yang sesuai posisi deklarasi.
// init_blocks.kt
// Mendemonstrasikan beberapa init block yang dieksekusi berurutan dan validasi.
class Produk(val nama: String, hargaAwal: Int) {
val harga: Int
init {
println("Init block 1: nama=$nama, hargaAwal=$hargaAwal")
}
val diskon: Int = if (hargaAwal > 100000) 10 else 0
init {
println("Init block 2: diskon dihitung = $diskon%")
harga = if (hargaAwal < 0) 0 else hargaAwal
}
init {
require(harga >= 0) { "Harga tidak boleh negatif: $harga" }
println("Init block 3: validasi selesai untuk $nama")
}
fun hargaAkhir(): Int = harga - (harga * diskon / 100)
}
fun main() {
println("Membuat produk A")
val a = Produk("Laptop", 1500000)
println("Harga akhir A: ${a.hargaAkhir()}")
println()
println("Membuat produk B")
val b = Produk("Buku Tulis", 5000)
println("Harga akhir B: ${b.hargaAkhir()}")
}Output yang Dihasilkan:
Membuat produk A
Init block 1: nama=Laptop, hargaAwal=1500000
Init block 2: diskon dihitung = 10%
Init block 3: validasi selesai untuk Laptop
Harga akhir A: 1350000
Membuat produk B
Init block 1: nama=Buku Tulis, hargaAwal=5000
Init block 2: diskon dihitung = 0%
Init block 3: validasi selesai untuk Buku Tulis
Harga akhir B: 5000Mari kita amati urutan eksekusinya:
Produk("Laptop", 1500000)dipanggil: primary constructor menerimanama="Laptop"danhargaAwal=1500000.- Init block 1 dieksekusi: mencatat nama dan hargaAwal. Property
diskonbelum dihitung karena dideklarasikan setelah init block ini. val diskon: Int = if (hargaAwal > 100000) 10 else 0: property initializer dievaluasi. KarenahargaAwal = 1500000 > 100000,diskon = 10.- Init block 2 dieksekusi: mencatat diskon yang sudah dihitung. Body menugaskan property
hargaberdasarkanhargaAwal; karenahargaAwal >= 0,harga = 1500000. - Init block 3 dieksekusi: memanggil
require(harga >= 0)yang melemparIllegalArgumentExceptionbila kondisi gagal. Karenaharga = 1500000 >= 0, validasi lolos dan pesan sukses tercetak. a.hargaAkhir()dipanggil: menghitung1500000 - (1500000 * 10 / 100) = 1350000. Propertydiskondanhargasudah final.
Validasi dengan require dan check #
Kotlin menyediakan dua function standar untuk validasi di dalam init block: require(condition) { message } melempar IllegalArgumentException, dan check(condition) { message } melempar IllegalStateException. Keduanya lazim digunakan untuk menegakkan invarian class sejak object dibuat. Bila kondisi gagal, object tidak akan pernah dikembalikan ke pemanggil.
Urutan Deklarasi Memengaruhi Hasil #
Karena init block dan property initializer berbagi urutan, sebuah property yang dideklarasikan setelah init block belum siap dipakai di dalam init block tersebut. Bila kita menukar posisi val diskon dan init block 2 pada contoh di atas, referensi diskon di dalam init block 2 akan gagal dikompilasi karena compiler mendeteksi penggunaan sebelum deklarasi.
Aturan praktis: deklarasikan property yang dibutuhkan sebuah init block sebelum init block tersebut. Bila property bergantung pada hasil init block, deklarasikan setelahnya. Dengan urutan yang konsisten, kode menjadi lebih mudah dibaca dan perilakunya dapat diprediksi.
Referensi Lanjutan #
- Classes and Inheritance — penjelasan resmi init block dan urutan inisialisasi
Ringkasan #
Init block adalah mekanisme utama Kotlin untuk menjalankan logika inisialisasi setelah primary constructor. Beberapa init block dieksekusi berurutan bersama property initializer sesuai posisi deklarasi. require dan check menjadi alat idiomatik untuk validasi di dalam init block. Pada chapter Property Accessors berikutnya, kita akan melihat bagaimana getter dan setter custom dapat melengkapi inisialisasi dengan kontrol lebih halus terhadap pembacaan dan penugasan property.