Setelah memahami dasar reflection, kita dapat menggunakannya untuk menginspeksi class secara mendalam. Pada chapter ini kita akan membedah properti KClass yang sering dipakai: simpleName, members, constructors, isData, isSealed, sealedSubclasses, dan memberProperties. Kita juga akan membuat instance via reflection menggunakan primaryConstructor.callBy(). Pemahaman ini sangat berguna untuk membangun library serialisasi, mapper, atau validator yang bekerja pada tipe apa pun.
Properti Penting KClass #
Berikut daftar properti KClass yang paling sering dipakai:
| Properti | Tipe | Kegunaan |
|---|---|---|
simpleName | String? | Nama class tanpa package. |
qualifiedName | String? | Nama lengkap dengan package. |
members | Collection<KCallable<*>> | Semua function dan property member. |
constructors | Collection<KFunction<T>> | Semua konstruktor. |
primaryConstructor | KFunction<T>? | Konstruktor utama. |
memberProperties | Collection<KProperty1<T, *>> | Property member. |
memberFunctions | Collection<KFunction<*>> | Function member. |
isData | Boolean | Apakah data class. |
isSealed | Boolean | Apakah sealed class/interface. |
isAbstract | Boolean | Apakah abstract. |
isCompanion | Boolean | Apakah companion object. |
sealedSubclasses | List<KClass<out T>> | Subtype langsung sealed class. |
Contoh Kode #
Kode berikut menginspeksi class sealed Hasil dan data class Pengguna, lalu membuat instance Pengguna via reflection:
// File: inspecting_classes.kt
// Mendemonstrasikan inspeksi class via KClass:
// simpleName, members, constructors, isData, isSealed, properties.
package com.example.inspeksi
import kotlin.reflect.KClass
import kotlin.reflect.full.memberProperties
import kotlin.reflect.full.primaryConstructor
sealed interface Hasil {
data class Sukses(val data: String) : Hasil
data class Gagal(val pesan: String, val kode: Int) : Hasil
}
data class Pengguna(val id: Long, val nama: String, val email: String)
fun ringkasKelas(kelas: KClass<*>) {
println("=== Inspeksi ${kelas.qualifiedName} ===")
println("simpleName: ${kelas.simpleName}")
println("isData: ${kelas.isData}")
println("isSealed: ${kelas.isSealed}")
println("isAbstract: ${kelas.isAbstract}")
println("isCompanion: ${kelas.isCompanion}")
println("constructors: ${kelas.constructors.size}")
println("primary constructor: ${kelas.primaryConstructor != null}")
println("members: ${kelas.members.size}")
println()
}
fun tampilkanProperti(kelas: KClass<*>) {
println("Properti pada ${kelas.simpleName}:")
for (prop in kelas.memberProperties) {
println(" - ${prop.name}: ${prop.returnType}")
}
println()
}
fun tampilkanSubSealed(kelas: KClass<*>) {
if (!kelas.isSealed) return
println("Subtype sealed ${kelas.simpleName}:")
for (sub in kelas.sealedSubclasses) {
println(" - ${sub.simpleName}")
}
println()
}
fun main() {
ringkasKelas(Hasil::class)
ringkasKelas(Pengguna::class)
tampilkanProperti(Pengguna::class)
tampilkanSubSealed(Hasil::class)
// Membuat instance via primary constructor reflection.
val ktor = Pengguna::class.primaryConstructor
if (ktor != null) {
val params = ktor.parameters
println("Parameter primary constructor:")
for (p in params) {
println(" - ${p.name}: ${p.type} (optional=${p.isOptional})")
}
val instance = ktor.callBy(
mapOf(
params[0] to 99L,
params[1] to "Dewi",
params[2] to "[email protected]"
)
)
println("\nInstance dibuat: $instance")
}
}Output yang Dihasilkan:
=== Inspeksi com.example.inspeksi.Hasil ===
simpleName: Hasil
isData: false
isSealed: true
isAbstract: false
isCompanion: false
constructors: 0
primary constructor: false
members: 3
=== Inspeksi com.example.inspeksi.Pengguna ===
simpleName: Pengguna
isData: true
isSealed: false
isAbstract: false
isCompanion: false
constructors: 1
primary constructor: true
members: 10
Properti pada Pengguna:
- email: kotlin.String
- id: kotlin.Long
- nama: kotlin.String
Subtype sealed Hasil:
- Gagal
- Sukses
Parameter primary constructor:
- id: kotlin.Long (optional=false)
- nama: kotlin.String (optional=false)
- email: kotlin.String (optional=false)
Instance dibuat: Pengguna(id=99, nama=Dewi, [email protected])Mari kita amati bagian-bagian penting di atas:
- Baris 8 (
sealed interface Hasil): sealed interface dengan dua subtype data class. Pemakaian sealed memungkinkan kita melihat daftar subtype viasealedSubclasses(baris 49). - Baris 14 (
data class Pengguna): data class sederhana dengan tiga property. Reflection akan menampilkanisData = truedan tiga property padamemberProperties. - Baris 17-27 (
fun ringkasKelas): fungsi generik yang menerimaKClass<*>(wildcard) dan mencetak ringkasan properti reflection. Cocok untuk diagnostik cepat. - Baris 36-41 (
fun tampilkanProperti): iterasimemberPropertiesdan mencetak nama serta tipe kembalian. Output padaPenggunamenampilkanemail,id, dannama(urutan tidak dijamin sama dengan deklarasi). - Baris 43-50 (
fun tampilkanSubSealed): khusus sealed class/interface.sealedSubclassesmengembalikan daftar subtype yang dideklarasikan dalam file yang sama atau file lain dengan anotasi@file:Suppress("SEALED_SUBCLASS_NOT_ON_SAME_FILE"). - Baris 60 (
ktor.parameters): daftar parameter primary constructor. Setiap parameter punyaname,type,isOptional, danisVararg. - Baris 67 (
ktor.callBy(mapOf(...))): membuat instance dengan menyediakan argumen sebagai map dariKParameterke nilai. Berbeda daricall()yang menggunakan positional args,callBy()mendukung optional parameter — bila sebuah parameterisOptional, kita dapat menghilangkannya dari map.
callBy vs call #
Method call() memakai argumen positional dan wajib menyertakan semua parameter (termasuk default). Method callBy() lebih fleksibel: menerima map dan otomatis memakai default value bila parameter tidak disertakan. Untuk data class dengan banyak parameter optional, callBy() lebih nyaman.
Iterasi members dan Filter #
Properti members mengembalikan semua callable member (function + property). Untuk filter, kita pakai filterIsInstance<T>() atau ekstensi declaredMemberProperties (hanya yang dideklarasikan di class ini, tidak termasuk warisan).
val props = kelas.memberProperties.filterIsInstance<KProperty1<Pengguna, *>>()
val dataCtor = kelas.constructors.first { it.parameters.isEmpty() }Sealed Subclasses untuk Pattern Matching #
Daftar sealedSubclasses memungkinkan kita menulis pattern matcher yang lengkap tanpa manual menulis when. Sebagai contoh, library serialisasi polymorphic memakai daftar ini untuk men-generate adapter setiap subtype.
Referensi Lanjutan #
- Reflection
— bagian class introspection dengan
KClasspada panduan reflection resmi - KClass API reference
— API reference
KClassuntuk memeriksa konstruktor, function, dan property
Selanjutnya #
Setelah menginspeksi class, kita akan mendalami property reflection secara spesifik — termasuk akses getter/setter dan properti isLateinit, isConst — topik chapter Property Reflection.