Operator is dan !is memeriksa tipe sebuah nilai saat runtime, sekaligus memberikan jaminan statis kepada kompilator. Setelah pemeriksaan is berhasil, kompilator Kotlin secara otomatis memperlakukan nilai sebagai tipe yang diperiksa tanpa memerlukan cast eksplisit. Mekanisme ini disebut smart cast. Pada chapter ini kita akan membandingkan pemeriksaan tipe manual dengan smart cast, mengulas kasus di mana smart cast bekerja dan tidak bekerja, serta mengintegrasikan is dengan konstruksi when untuk pencocokan tipe.

Dasar Penggunaan is dan !is #

Operator is mengembalikan true jika nilai adalah instance dari tipe yang diperiksa (atau subtype-nya). Operator !is adalah negasinya, mengembalikan true jika nilai bukan instance tipe tersebut. Berikut ringkasan perilaku:

EkspresiHasilCatatan
"halo" is StringtrueString literal memang String
"halo" is CharSequencetrueString adalah subtype CharSequence
42 is StringfalseInt bukan String
42 !is StringtrueNegasi is
42 is NumbertrueInt adalah subtype Number
null is StringfalsePemeriksaan aman terhadap null

Operator is tidak pernah melempar galat. Pemeriksaan null is String menghasilkan false daripada NullPointerException, karena kompilator menyuntikkan pemeriksaan null internal.

Smart Cast Setelah Pemeriksaan is #

Setelah pemeriksaan is berhasil pada blok kondisional, kompilator Kotlin memperlakukan variabel sebagai tipe yang diperiksa tanpa perlu as. Inilah fitur smart cast yang membedakan Kotlin dari Java. Berikut perbandingan:

kotlin Contoh: tanpa dan dengan smart cast
// Tanpa smart cast (perlu cast eksplisit)
val data: Any = "halo"
if (data is String) {
    val panjang = (data as String).length
}

// Dengan smart cast (cast otomatis)
if (data is String) {
    val panjang = data.length
}

Kedua blok di atas menghasilkan perilaku yang sama, tetapi blok kedua jauh lebih ringkas karena kompilator Kotlin menyimpulkan bahwa setelah data is String mengembalikan true, variabel data dapat diakses sebagai String.

kotlin
fun main() {
    val data: Any = "Halo, Kotlin"

    if (data is String) {
        println("Panjang teks: ${data.length}")
        println("Huruf besar: ${data.uppercase()}")
    }

    if (data !is Int) {
        println("data bukan Int")
    }

    val input: Any = 42
    if (input is Int) {
        println("input + 8 = ${input + 8}")
    }

    // Smart cast manual dengan when
    val nilai: Any = 3.14
    val deskripsi = when (nilai) {
        is Int -> "bilangan bulat ${nilai.toInt()}"
        is Double -> "bilangan desimal $nilai"
        is String -> "teks $nilai"
        else -> "tipe lain"
    }
    println("Deskripsi: $deskripsi")

    // Tanpa smart cast (perlu cast manual)
    val objek: Any = "Budi"
    val panjang = (objek as String).length
    println("Panjang objek: $panjang")

    // Smart cast dengan nullable
    val nama: String? = "Siti"
    if (nama != null) {
        println("Nama: ${nama.uppercase()}")
    }
}

Output yang Dihasilkan:

text
Panjang teks: 12
Huruf besar: HALO, KOTLIN
data bukan Int
input + 8 = 50
Deskripsi: bilangan desimal 3.14
Panjang objek: 4
Nama: SITI

Mari kita uraikan baris-baris penting pada contoh di atas:

  1. if (data is String) { ... data.length ... } memanfaatkan smart cast. Di dalam blok, kompilator memperlakukan data sebagai String, sehingga data.length dan data.uppercase() tersedia tanpa as String.
  2. if (data !is Int) adalah negasi dari is. Blok ini hanya dieksekusi jika data bukan Int. Negasi tidak memicu smart cast, karena tipe yang diketahui hanya apa yang bukan, bukan apa yang adalah.
  3. when (nilai) { is Int -> ...; is Double -> ... } memanfaatkan smart cast per-branch. Di setiap cabang, nilai diperlakukan sebagai tipe yang cocok. Inilah pola idiomatis Kotlin untuk type narrowing multi-cabang.
  4. (objek as String).length melakukan cast eksplisit tanpa smart cast. Pendekatan ini rentan ClassCastException jika objek ternyata bukan String. Hindari bila smart cast memungkinkan.
  5. if (nama != null) { nama.uppercase() } memanfaatkan smart cast untuk nullability. Setelah pemeriksaan nama != null berhasil, nama diperlakukan sebagai String non-null di dalam blok. Ini adalah bentuk smart cast paling sering dipakai sehari-hari.

Kapan Smart Cast Tidak Bekerja #

Smart cast bergantung pada jaminan bahwa variabel tidak berubah tipe antara pemeriksaan dan pemakaian. Karena itu, smart cast tidak bekerja pada beberapa kasus berikut:

KasusAlasan
Property var di classThread lain dapat mengubah nilai di antara pemeriksaan dan pemakaian
Property yang di-overrideTidak ada jaminan subclass tidak mengubah perilaku getter
Variabel lokal yang ditangkap lambdaLambda dapat memodifikasi variabel dari luar scope
Variabel open val yang kompleksGetter mungkin mengembalikan instance berbeda setiap pemanggilan

Untuk kasus di atas, lakukan cast eksplisit dengan as (unsafe) atau as? (safe). Kita akan membahas kedua operator tersebut pada chapter berikutnya.

Integrasi dengan when #

Konstruksi when adalah cara paling ringkas untuk menangani banyak tipe sekaligus. Setiap cabang dapat memuat pemeriksaan is, dan smart cast aktif di dalam cabang tersebut. Pemeriksaan dilakukan berurutan dari atas ke bawah, sehingga tipe yang lebih spesifik harus ditempatkan sebelum tipe yang lebih umum. Misalnya, is Int harus muncul sebelum is Number agar tidak pernah terlewat.

Referensi Lanjutan #

  • Type casts — panduan resmi operator is dan !is untuk runtime type check
  • Smart casts — smart cast yang dipicu oleh is pada konteks tertentu

Dengan memahami is, !is, dan smart cast, kita siap mempelajari operator cast as dan as? yang memberi kontrol lebih langsung atas konversi tipe pada chapter berikutnya.