Type erasure membuat type parameter generic hilang saat runtime, sehingga kita tidak dapat melakukan if (item is T). Untuk banyak kasus, ini cukup membatasi — kita mungkin ingin menulis function yang memfilter elemen berdasarkan tipe, atau function yang mencetak nama tipe generic. Kotlin menyediakan reified type parameters untuk mengatasi batasan ini. Reified hanya bekerja pada function inline karena kompilator menggabungkan function ke dalam pemanggil, sehingga type argument konkret tersedia di lokasi pemanggilan. Pada chapter ini kita akan mempelajari cara mendeklarasikan function dengan reified, melihat apa yang menjadi mungkin, serta memahami batasan yang masih tersisa.

Apa Itu Reified #

Keyword reified pada type parameter berarti kompilator mempertahankan informasi tipe generic di lokasi pemanggilan. Karena function inline disalin ke setiap pemanggil, kompilator mengetahui type argument konkret di setiap titik pemanggilan. Hal ini memungkinkan operasi yang biasanya dilarang oleh type erasure.

kotlin Function reified dasar
inline fun <reified T> namaTipe(): String = T::class.simpleName ?: "<anonim>"

Function namaTipe<Int>() mengembalikan "Int" karena kompilator menggabungkan function dengan T = Int ke pemanggil. Tanpa reified dan inline, type erasure akan menghapus T dan function tidak akan tahu tipe apa yang dipakai.

Mengapa Reified Memerlukan Inline #

Function non-inline tidak dapat memiliki reified type parameter karena pemanggil function tidak mengetahui type argument yang dipakai oleh pemanggil. Function inline disalin ke pemanggil, sehingga type argument yang ditulis pemanggil tersedia di lokasi kode. Hal ini menjelaskan mengapa reified selalu didahului oleh inline.

Operasi yang Memungkinkan Reified #

Reified membuka banyak operasi yang biasanya dilarang oleh type erasure:

OperasiTanpa ReifiedDengan Reified
item is TTidakYa
T::class.simpleNameTidakYa
Array<T> (kreasi)TidakYa (untuk size)
javaClass<T>()TidakYa
Refleksi tipe genericTidakYa

Contoh Lengkap dengan Reified #

Mari kita lihat beberapa penggunaan reified dalam praktek:

kotlin
// Chapter: reified Type Parameters
// inline fun <reified T>: type parameter tetap tersedia saat runtime.

inline fun <reified T> namaTipe(): String = T::class.simpleName ?: "<anonim>"

inline fun <reified T> adalahTipe(item: Any): Boolean = item is T

inline fun <reified T> filterTipe(daftar: List<Any>): List<T> {
    val hasil = mutableListOf<T>()
    for (e in daftar) {
        if (e is T) hasil.add(e)
    }
    return hasil
}

inline fun <reified T> buatList(kosong: T): List<T> {
    return listOf(kosong, kosong)
}

class Token(val nilai: String)

data class Pesanan(val id: Int, val nominal: Int)

fun main() {
    println("Tipe Int: ${namaTipe<Int>()}")
    println("Tipe String: ${namaTipe<String>()}")
    println("Tipe Token: ${namaTipe<Token>()}")
    println("Tipe Pesanan: ${namaTipe<Pesanan>()}")

    println("10 adalah Int? ${adalahTipe<Int>(10)}")
    println("10 adalah String? ${adalahTipe<String>(10)}")
    println("\"Halo\" adalah String? ${adalahTipe<String>("Halo")}")

    val campur: List<Any> = listOf(1, "dua", 3, "empat", 5.0, "enam")
    val hanyaString = filterTipe<String>(campur)
    println("String saja: $hanyaString")
    val hanyaInt = filterTipe<Int>(campur)
    println("Int saja: $hanyaInt")

    val arr = buatList("[kosong]")
    println("Daftar String default: $arr")
    val arrInt = buatList(0)
    println("Daftar Int default: $arrInt")
}

Output yang Dihasilkan:

text
Tipe Int: Int
Tipe String: String
Tipe Token: Token
Tipe Pesanan: Pesanan
10 adalah Int? true
10 adalah String? false
"Halo" adalah String? true
String saja: [dua, empat, enam]
Int saja: [1, 3]
Daftar String default: [[kosong], [kosong]]
Daftar Int default: [0, 0]

Mari kita telaah baris-baris kode di atas:

  1. inline fun <reified T> namaTipe(): function inline dengan type parameter reified. Function mengembalikan nama sederhana kelas T melalui refleksi.
  2. namaTipe<Int>(): pemanggil menentukan type argument Int. Karena function inline, kompilator mengganti T dengan Int di lokasi pemanggil, sehingga T::class.simpleName menjadi Int::class.simpleName.
  3. inline fun <reified T> adalahTipe(item: Any): Boolean = item is T: function yang memeriksa apakah item bertipe T. Tanpa reified, operasi is T dilarang karena type erasure.
  4. adalahTipe<Int>(10): pemanggilan valid. Kompilator mengganti T dengan Int, sehingga pemeriksaan menjadi item is Int.
  5. inline fun <reified T> filterTipe(daftar: List<Any>): List<T>: function generic yang memfilter elemen berdasarkan tipe. Hasil adalah List<T> — list berisi elemen yang dipastikan bertipe T.
  6. filterTipe<String>(campur): kompilator mengganti T dengan String. Setiap elemen diperiksa dengan is String, dan yang lolos dimasukkan ke hasil.
  7. inline fun <reified T> buatList(kosong: T): List<T>: function sederhana yang membuat list dengan dua elemen identik. Tidak benar-benar memanfaatkan reified, tetapi ditunjukkan sebagai contoh tambahan.
  8. buatList("[kosong]"): kompilator menyimpulkan T = String dari argumen "[kosong]". Hasilnya List<String> berisi dua elemen string.

Batasan Reified #

Meskipun reified membuka banyak kemungkinan, ada batasan yang masih berlaku:

  1. Tidak dapat dipakai pada class generic — hanya pada function inline.
  2. Tidak dapat dipakai untuk membuat array Array<T> dengan size yang bergantung pada T — array Java memerlukan informasi tipe runtime yang reified, dan reified Kotlin tidak sepenuhnya menggantikan array reifikasi JVM.
  3. Tidak dapat dipanggil dari kode Java — Java tidak memahami reified.

Untuk kasus yang memerlukan pemeriksaan tipe generic kompleks, kombinasikan reified dengan KClass<T> atau pustaka refleksi seperti kotlin-reflect.

Penggunaan Nyata Reified #

Reified banyak dipakai di standard library Kotlin dan library populer. Contoh paling dikenal adalah filterIsInstance<T>() pada koleksi — function inline dengan reified yang memfilter elemen berdasarkan tipe. Ktor memakai reified untuk mengekstrak parameter dari URL. Koin memakai reified untuk resolusi dependensi berbasis tipe. Pola umum ini menjadikan reified salah satu fitur Kotlin yang paling sering dijumpai dalam kode produksi.

Referensi Lanjutan #

Pada chapter Designing Generic APIs kita akan melihat bagaimana memadukan semua konsep generics — type parameter, bound, variance, reified — menjadi desain API yang elegan dan aman.