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:

OperatorNamaGagal?Hasil saat gagalCocok untuk
asUnsafe castYa, lempar ClassCastExceptionExceptionTipe sudah diyakini benar
as?Safe castTidaknullTipe 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 #

kotlin
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:

text
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:

  1. objek as String melakukan unsafe cast. Karena objek sebenarnya berisi "Halo", cast berhasil dan teks bernilai "Halo". Bila objek berisi nilai lain, baris ini akan melempar ClassCastException dan menghentikan eksekusi.
  2. objek as? String melakukan safe cast. Hasilnya "Halo" (tipe String?). Bila objek bukan String, hasilnya null, bukan exception. Karena itu tipe kembalian selalu nullable.
  3. angka as? String mengembalikan null karena 42 (Int) tidak dapat dikonversi ke String. Eksekusi tetap berlanjut tanpa interupsi.
  4. (objek as? String)?.length mengombinasikan safe cast dengan safe call. Bila objek adalah String, kita mendapatkan panjangnya. Bila bukan, seluruh ekspresi menghasilkan null. Pola ini sangat umum pada pengolahan JSON yang tipenya dinamis.
  5. 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 sebagai Any?.
  6. when (val x: Any = "123") { is String -> x.toIntOrNull() ... } menggunakan smart cast (lihat chapter is/!is) alih-alih as. Pendekatan ini lebih aman karena kompilator memverifikasi tipe sebelum pemakaian.
  7. try { angka as String } catch (e: ClassCastException) menangkap exception dari unsafe cast. Pola ini jarang dianjurkan — as? lebih ringkas dan tidak memerlukan blok try-catch.

Pola Type Narrowing yang Aman #

Pada kode produksi, hindari as bila memungkinkan. Berikut hierarki preferensi dari paling aman hingga paling berisiko:

  1. is + smart cast — paling aman, verifikasi tipe dan akses property dijamin konsisten oleh kompilator.
  2. as? + safe call (?.) — aman, mengembalikan null saat gagal; cocok untuk input tak terduga.
  3. 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 berisi Int, karena JVM tidak dapat memverifikasi parameter tipe.
  • Pemakaian elemen yang sebenarnya berbeda tipe baru melempar ClassCastException saat 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.

kotlin Contoh: reified type parameter
inline fun <reified T> isType(value: Any): Boolean = value is T

println(isType<String>("halo")) // true
println(isType<Int>("halo"))    // false

Referensi Lanjutan #

  • Type casts — panduan resmi unsafe cast as dan safe cast as?
  • 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.