Operator cast mengubah representasi tipe statis sebuah nilai. Kotlin menyediakan dua bentuk: as yang bersifat unsafe (melempar ClassCastException jika gagal) dan as? yang bersifat safe (mengembalikan null jika gagal). Pilihan antara keduanya menentukan ketahanan program terhadap data tak terduga. Pada chapter ini kita akan membahas kapan menggunakan as, kapan beralih ke as?, serta pola type narrowing yang aman dipakai pada kode produksi.
Perbedaan as dan as? #
Berikut ringkasan kedua operator cast:
| Operator | Nama | Gagal? | Hasil saat gagal | Cocok untuk |
|---|---|---|---|---|
as | Unsafe cast | Ya, lempar ClassCastException | Exception | Tipe sudah diyakini benar |
as? | Safe cast | Tidak | null | Tipe tidak pasti dari input eksternal |
Operator as cocok dipakai ketika kompilator membuktikan tipe target pasti benar, atau ketika kita ingin program gagal cepat (fail-fast) jika asumsi dilanggar. Operator as? cocok dipakai ketika input berasal dari sumber yang tidak terkontrol (JSON, database, library Java) dan kita ingin menangani ketidakcocokan dengan nilai default atau cabang alternatif.
Cast Langsung dan Aman #
fun main() {
val objek: Any = "Halo"
val teks: String = objek as String
println("Hasil as: $teks, panjang ${teks.length}")
val aman: String? = objek as? String
println("Hasil as? String: $aman")
val angka: Any = 42
val gagal: String? = angka as? String
println("angka as? String: $gagal")
val masuk: Int? = (objek as? String)?.length
println("Panjang dari objek: $masuk")
val daftar: Any = listOf(1, 2, 3)
val itemList = daftar as? List<*>
println("Daftar aman: $itemList, ukuran ${itemList?.size}")
val konversi = when (val x: Any = "123") {
is String -> x.toIntOrNull()
is Int -> x
else -> null
}
println("Konversi multi-tipe: $konversi")
try {
val salah: String = angka as String
println(salah)
} catch (e: ClassCastException) {
println("ClassCastException: ${e.message}")
}
}Output yang Dihasilkan:
Hasil as: Halo, panjang 4
Hasil as? String: Halo
angka as? String: null
Panjang dari objek: 4
Daftar aman: [1, 2, 3], ukuran 3
Konversi multi-tipe: 123
ClassCastException: class java.lang.Integer cannot be cast to class java.lang.String (java.lang.Integer and java.lang.String are in module java.base of loader 'bootstrap')Mari kita uraikan baris-baris penting pada contoh di atas:
objek as Stringmelakukan unsafe cast. Karenaobjeksebenarnya berisi"Halo", cast berhasil danteksbernilai"Halo". Bilaobjekberisi nilai lain, baris ini akan melemparClassCastExceptiondan menghentikan eksekusi.objek as? Stringmelakukan safe cast. Hasilnya"Halo"(tipeString?). BilaobjekbukanString, hasilnyanull, bukan exception. Karena itu tipe kembalian selalu nullable.angka as? Stringmengembalikannullkarena42(Int) tidak dapat dikonversi keString. Eksekusi tetap berlanjut tanpa interupsi.(objek as? String)?.lengthmengombinasikan safe cast dengan safe call. BilaobjekadalahString, kita mendapatkan panjangnya. Bila bukan, seluruh ekspresi menghasilkannull. Pola ini sangat umum pada pengolahan JSON yang tipenya dinamis.daftar as? List<*>melakukan safe cast ke list generic. Tanda*(star projection) menyatakan tipe elemen tidak diketahui, namun struktur list itu sendiri dapat dipakai. Hasilnya adalah list yang dapat diiterasi, namun elemennya hanya bisa diperlakukan sebagaiAny?.when (val x: Any = "123") { is String -> x.toIntOrNull() ... }menggunakan smart cast (lihat chapteris/!is) alih-alihas. Pendekatan ini lebih aman karena kompilator memverifikasi tipe sebelum pemakaian.try { angka as String } catch (e: ClassCastException)menangkap exception dari unsafe cast. Pola ini jarang dianjurkan —as?lebih ringkas dan tidak memerlukan bloktry-catch.
Pola Type Narrowing yang Aman #
Pada kode produksi, hindari as bila memungkinkan. Berikut hierarki preferensi dari paling aman hingga paling berisiko:
is+ smart cast — paling aman, verifikasi tipe dan akses property dijamin konsisten oleh kompilator.as?+ safe call (?.) — aman, mengembalikannullsaat gagal; cocok untuk input tak terduga.as— paling berisiko, melempar exception saat gagal; hanya untuk kasus di mana tipe target sudah pasti benar.
Untuk deserialisasi JSON yang kompleks, gunakan library berbasis refleksi seperti kotlinx.serialization yang menghasilkan objek bertipe kuat tanpa perlu cast manual.
Cast pada Generic dan Star Projection #
Cast pada tipe generic memerlukan perhatian khusus karena type erasure pada JVM. Pada runtime, List<String> dan List<Int> menjadi List tanpa parameter tipe. Akibatnya:
list as List<String>selalu berhasil pada saat runtime bahkan jika list sebenarnya berisiInt, karena JVM tidak dapat memverifikasi parameter tipe.- Pemakaian elemen yang sebenarnya berbeda tipe baru melempar
ClassCastExceptionsaat elemen diakses.
Untuk memeriksa tipe generic, gunakan reified type parameter pada inline function. Konstruksi ini menyimpan tipe pada saat kompilasi sehingga pemeriksaan is dapat bekerja untuk parameter tipe generic.
inline fun <reified T> isType(value: Any): Boolean = value is T
println(isType<String>("halo")) // true
println(isType<Int>("halo")) // falseReferensi Lanjutan #
- Type casts
— panduan resmi unsafe cast
asdan safe castas? - Smart casts — konteks smart cast yang meminimalkan kebutuhan unsafe cast
Dengan memahami kedua operator cast beserta pola penggunaannya, kita siap mempelajari operator overloading yang memungkinkan kita mendefinisikan perilaku +, [], in, dan operator lain pada class buatan sendiri.