Smart cast adalah fitur Kotlin yang membuat kompilator menyesuaikan tipe statis sebuah variabel berdasarkan pengecekan yang sudah dilakukan sebelumnya. Setelah if (x is String), kompilator memperlakukan x sebagai String di dalam blok tersebut tanpa perlu cast eksplisit. Demikian pula, setelah if (x != null), variabel nullable menjadi non-nullable di dalam blok. Fitur ini mengurangi boilerplate dan membuat kode lebih ringkas sekaligus aman. Pada chapter ini kita akan membedah smart cast pada if, when, &&, dan while, serta batasan-batasan praktisnya.

Smart Cast pada Operator is #

Operator is menguji apakah sebuah nilai merupakan tipe tertentu. Setelah pengujian positif, kompilator mempersempit tipe statis variabel pada cabang yang sesuai.

kotlin
fun main() {
    val nilai: Any = "Belajar Kotlin"

    if (nilai is String) {
        println("Panjang string: ${nilai.length}")
        println("Huruf besar: ${nilai.uppercase()}")
        println("Huruf kecil: ${nilai.lowercase()}")
    }

    val angka: Any = 42
    if (angka is Int) {
        val kuadrat: Int = angka * angka
        println("Kuadrat: $kuadrat")
        println("Apakah positif? ${angka > 0}")
    }

    when (nilai) {
        is String -> println("String dengan panjang ${nilai.length}")
        is Int -> println("Integer: $nilai")
        is Boolean -> println("Boolean: $nilai")
        else -> println("Tipe lain")
    }

    val x: Any? = "Dewi"
    if (x != null && x is String) {
        println("String non-null: ${x.uppercase()}")
    }

    val data: Any = 3.14
    if (data is Double && data > 3.0) {
        println("Double besar: $data")
    }

    val daftar: List<Any> = listOf("Budi", 25, true, "Siti")
    for (item in daftar) {
        when (item) {
            is String -> println("String: '$item', panjang ${item.length}")
            is Int -> println("Int: $item, dua kalinya ${item * 2}")
            is Boolean -> println("Boolean: $item, negasi ${!item}")
        }
    }

    val masukan: Any? = null
    if (masukan is String) {
        println("Tidak akan tercetak: ${masukan.length}")
    } else {
        println("Masukan bukan String (atau null)")
    }

    var kondisi: Any? = "Halo"
    while (kondisi is String) {
        println("Kondisi masih String: $kondisi")
        kondisi = 0
    }
    println("Akhir loop")
}

Output yang Dihasilkan:

text
Panjang string: 14
Huruf besar: BELAJAR KOTLIN
Huruf kecil: belajar kotlin
Kuadrat: 1764
Apakah positif? true
String dengan panjang 14
String non-null: DEWI
Double besar: 3.14
String: 'Budi', panjang 4
Int: 25, dua kalinya 50
Boolean: true, negasi false
String: 'Siti', panjang 4
Masukan bukan String (atau null)
Kondisi masih String: Halo
Akhir loop

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

  1. Baris 4 (if (nilai is String)): setelah is String terbukti benar, kompilator memperlakukan nilai sebagai String di dalam blok. Karena itu .length, .uppercase(), dan .lowercase() dapat dipanggil tanpa cast eksplisit.
  2. Baris 11 (if (angka is Int)): smart cast juga bekerja untuk tipe numerik. angka * angka sekarang diizinkan karena kompilator tahu angka adalah Int.
  3. Baris 16-21 (when (nilai) { is String -> ... }): setiap cabang when yang berbasis is mempersempit tipe variabel pada cabang tersebut. nilai.length dapat dipanggil tanpa cast.
  4. Baris 24 (if (x != null && x is String)): smart cast bekerja pada kondisi gabungan &&. Karena && mengevaluasi kiri-ke-kanan dan singkat-sirkuit (short-circuit), cabang x is String hanya dievaluasi bila x != null. Karena itu di dalam blok, x diperlakukan sebagai String non-nullable.
  5. Baris 29 (if (data is Double && data > 3.0)): kombinasi smart cast dengan operasi numerik. Setelah is Double benar, perbandingan data > 3.0 menggunakan tipe Double.
  6. Baris 33-39 (iterasi for (item in daftar)): smart cast bekerja pada variabel loop. Setiap cabang when mempersempit item ke tipe yang sesuai sehingga method spesifik seperti .length atau !item dapat dipanggil.
  7. Baris 42-46 (if (masukan is String)): variabel bertipe Any? — bila masukan adalah null, kondisi is String mengembalikan false (bukan melempar exception), sehingga cabang else yang dijalankan.
  8. Baris 49-52 (while (kondisi is String)): smart cast juga berlaku pada kondisi while. Di dalam loop, kondisi diperlakukan sebagai String. Loop berhenti setelah kondisi ditetapkan ke 0 (bukan String).

Smart Cast Nullability #

Selain narrowing tipe, smart cast juga bekerja pada nullability. Setelah if (x != null), variabel x bertipe String? akan diperlakukan sebagai String non-nullable di dalam blok. Pengecekan != null ini menjadi cara paling alami untuk membuka akses langsung pada variabel nullable.

kotlin Smart cast untuk nullability
val nama: String? = ambilNama()
if (nama != null) {
    println(nama.length)
}

Pada blok if, nama.length dipanggil tanpa ?. karena kompilator sudah mempersempit tipe ke String.

Batasan Smart Cast #

Smart cast tidak diterapkan dalam beberapa situasi berikut:

  • Properti var yang dapat diakses publik dan dapat diubah dari luar.
  • Properti open yang dapat di-override di subclass.
  • Variabel yang nilainya berasal dari delegate property kompleks.
  • Variabel lokal var yang dipakai dalam lambda yang ditangkap (captured), karena dapat berubah antara pengecekan dan pemakaian.

Pada kasus-kasus tersebut, kompilator meminta kita melakukan cast eksplisit menggunakan as atau as? (lihat chapter Safe Cast).

Referensi Lanjutan #

  • Smart casts — panduan resmi smart cast otomatis setelah pengecekan is
  • Type casts — konteks is dan as pada panduan typecasts

Setelah memahami smart cast, langkah berikutnya adalah mempelajari pola pengecekan null dengan != null dan == null secara lebih dalam — topik yang akan kita bahas pada chapter Null Checking.