🔍 Reflection
1. Giới thiệu
Phần tiêu đề “1. Giới thiệu”Reflection cho phép kiểm tra và thao tác với code tại runtime - xem classes, functions, properties, và gọi chúng dynamically.
2. Setup
Phần tiêu đề “2. Setup”Thêm dependency trong build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.0")}3. KClass - Class References
Phần tiêu đề “3. KClass - Class References”import kotlin.reflect.KClassimport kotlin.reflect.full.*
data class User(val name: String, val age: Int)
fun main() { // Lấy KClass từ class val userClass: KClass<User> = User::class
// Lấy KClass từ instance val user = User("Alice", 25) val classFromInstance: KClass<out User> = user::class
// Basic info println("Class name: ${userClass.simpleName}") // User println("Qualified: ${userClass.qualifiedName}") // com.example.User println("Is data: ${userClass.isData}") // true println("Is abstract: ${userClass.isAbstract}") // false}Class properties và functions
Phần tiêu đề “Class properties và functions”import kotlin.reflect.full.*
data class Person(val name: String, var age: Int) { fun greet() = "Hello, $name!" private fun secret() = "Shhh"}
fun main() { val kClass = Person::class
// Properties println("=== Properties ===") kClass.memberProperties.forEach { prop -> println("${prop.name}: ${prop.returnType}") } // name: kotlin.String // age: kotlin.Int
// Functions println("\n=== Functions ===") kClass.memberFunctions.forEach { func -> println("${func.name}(${func.parameters.joinToString { it.name ?: "receiver" }})") } // greet(receiver) // secret(receiver) // ...
// Constructors println("\n=== Constructors ===") kClass.constructors.forEach { ctor -> println(ctor.parameters.map { "${it.name}: ${it.type}" }) }}4. KCallable - Functions và Properties
Phần tiêu đề “4. KCallable - Functions và Properties”Function references
Phần tiêu đề “Function references”import kotlin.reflect.KFunction
fun greet(name: String): String = "Hello, $name!"
fun main() { // Function reference val greetRef: KFunction<String> = ::greet
// Call via reference println(greetRef.call("Alice")) // Hello, Alice!
// Function info println("Name: ${greetRef.name}") println("Parameters: ${greetRef.parameters.map { it.name }}") println("Return type: ${greetRef.returnType}")}Property references
Phần tiêu đề “Property references”import kotlin.reflect.KPropertyimport kotlin.reflect.KMutableProperty
data class User(val name: String, var age: Int)
fun main() { val user = User("Alice", 25)
// Property reference val nameRef: KProperty<String> = User::name val ageRef: KMutableProperty<Int> = User::age
// Get values println(nameRef.get(user)) // Alice println(ageRef.get(user)) // 25
// Set value (mutable only) ageRef.set(user, 30) println(user.age) // 30
// Property info println("Name: ${nameRef.name}") println("Is const: ${nameRef.isConst}") println("Is lateinit: ${nameRef.isLateinit}")}5. Dynamic Invocation
Phần tiêu đề “5. Dynamic Invocation”Gọi function dynamically
Phần tiêu đề “Gọi function dynamically”import kotlin.reflect.full.*
class Calculator { fun add(a: Int, b: Int): Int = a + b fun subtract(a: Int, b: Int): Int = a - b fun multiply(a: Int, b: Int): Int = a * b}
fun main() { val calc = Calculator() val kClass = calc::class
// Tìm function theo tên val addFunc = kClass.memberFunctions.find { it.name == "add" }!!
// Gọi function val result = addFunc.call(calc, 10, 5) println("10 + 5 = $result") // 10 + 5 = 15
// Gọi function bất kỳ theo tên fun calculate(operation: String, a: Int, b: Int): Int { val func = kClass.memberFunctions.find { it.name == operation } ?: throw IllegalArgumentException("Unknown operation: $operation") return func.call(calc, a, b) as Int }
println(calculate("add", 5, 3)) // 8 println(calculate("subtract", 5, 3)) // 2 println(calculate("multiply", 5, 3)) // 15}Set property dynamically
Phần tiêu đề “Set property dynamically”import kotlin.reflect.full.*import kotlin.reflect.jvm.isAccessible
class Config { var host: String = "localhost" var port: Int = 8080 private var secret: String = "hidden"}
fun main() { val config = Config() val kClass = config::class
// Set public property val hostProp = kClass.memberProperties.find { it.name == "host" } as kotlin.reflect.KMutableProperty1<Config, String> hostProp.set(config, "example.com") println(config.host) // example.com
// Access private property val secretProp = kClass.memberProperties.find { it.name == "secret" }!! secretProp.isAccessible = true println(secretProp.get(config)) // hidden}6. Create Instances Dynamically
Phần tiêu đề “6. Create Instances Dynamically”import kotlin.reflect.full.*
data class User(val name: String, val age: Int = 0)
fun main() { val kClass = User::class
// Find primary constructor val constructor = kClass.primaryConstructor!!
// Create with positional args val user1 = constructor.call("Alice", 25) println(user1) // User(name=Alice, age=25)
// Create with named args (respects defaults) val user2 = constructor.callBy(mapOf( constructor.parameters[0] to "Bob" // age uses default value )) println(user2) // User(name=Bob, age=0)}Factory pattern với reflection
Phần tiêu đề “Factory pattern với reflection”import kotlin.reflect.KClassimport kotlin.reflect.full.*
interface Repositoryclass UserRepository : Repositoryclass ProductRepository : Repository
object RepositoryFactory { private val registry = mutableMapOf<String, KClass<out Repository>>()
init { register("user", UserRepository::class) register("product", ProductRepository::class) }
fun register(name: String, kClass: KClass<out Repository>) { registry[name] = kClass }
fun create(name: String): Repository { val kClass = registry[name] ?: throw IllegalArgumentException("Unknown repository: $name") return kClass.createInstance() }}
fun main() { val userRepo = RepositoryFactory.create("user") println(userRepo::class.simpleName) // UserRepository}7. Annotations với Reflection
Phần tiêu đề “7. Annotations với Reflection”import kotlin.reflect.full.*
@Target(AnnotationTarget.PROPERTY)@Retention(AnnotationRetention.RUNTIME)annotation class JsonField(val name: String)
@Target(AnnotationTarget.PROPERTY)@Retention(AnnotationRetention.RUNTIME)annotation class JsonIgnore
data class User( @JsonField("user_id") val id: Int,
@JsonField("user_name") val name: String,
@JsonIgnore val password: String)
fun toJson(obj: Any): String { val kClass = obj::class val properties = kClass.memberProperties .filterNot { it.findAnnotation<JsonIgnore>() != null } .map { prop -> val jsonName = prop.findAnnotation<JsonField>()?.name ?: prop.name val value = prop.getter.call(obj) val valueStr = if (value is String) "\"$value\"" else value.toString() "\"$jsonName\": $valueStr" }
return "{ ${properties.joinToString(", ")} }"}
fun main() { val user = User(1, "Alice", "secret123") println(toJson(user)) // { "user_id": 1, "user_name": "Alice" }}8. Type Information
Phần tiêu đề “8. Type Information”import kotlin.reflect.full.*import kotlin.reflect.typeOf
// Reified type để giữ type info at runtimeinline fun <reified T> printType() { val type = typeOf<T>() println("Type: $type") println("Classifier: ${type.classifier}") println("Arguments: ${type.arguments}") println("Is nullable: ${type.isMarkedNullable}")}
fun main() { printType<String>() // Type: kotlin.String // Classifier: class kotlin.String // Arguments: [] // Is nullable: false
printType<List<Int>>() // Type: kotlin.collections.List<kotlin.Int> // Classifier: class kotlin.collections.List // Arguments: [kotlin.Int]
printType<Map<String, List<Int>>>() // Type: kotlin.collections.Map<kotlin.String, kotlin.collections.List<kotlin.Int>>}9. Use Cases thực tế
Phần tiêu đề “9. Use Cases thực tế”Simple Dependency Injection
Phần tiêu đề “Simple Dependency Injection”import kotlin.reflect.KClassimport kotlin.reflect.full.*
@Target(AnnotationTarget.CONSTRUCTOR)annotation class Inject
class Logger { fun log(message: String) = println("[LOG] $message")}
class Database { fun query(sql: String) = println("[DB] $sql")}
class UserService @Inject constructor( private val logger: Logger, private val database: Database) { fun createUser(name: String) { logger.log("Creating user: $name") database.query("INSERT INTO users (name) VALUES ('$name')") }}
object Container { private val instances = mutableMapOf<KClass<*>, Any>()
init { register(Logger::class) { Logger() } register(Database::class) { Database() } }
fun <T : Any> register(kClass: KClass<T>, factory: () -> T) { instances[kClass] = factory() }
@Suppress("UNCHECKED_CAST") fun <T : Any> resolve(kClass: KClass<T>): T { return instances[kClass] as? T ?: createInstance(kClass) }
private fun <T : Any> createInstance(kClass: KClass<T>): T { val constructor = kClass.constructors .find { it.findAnnotation<Inject>() != null } ?: kClass.primaryConstructor ?: throw IllegalArgumentException("No suitable constructor")
val args = constructor.parameters.map { param -> resolve(param.type.classifier as KClass<*>) }
return constructor.call(*args.toTypedArray()) }}
fun main() { val userService = Container.resolve(UserService::class) userService.createUser("Alice") // [LOG] Creating user: Alice // [DB] INSERT INTO users (name) VALUES ('Alice')}Object Mapping
Phần tiêu đề “Object Mapping”import kotlin.reflect.full.*
data class UserDto(val id: Int, val name: String, val email: String)data class UserEntity(val id: Int, val name: String, val email: String, val createdAt: Long)
inline fun <reified T : Any> map(source: Any): T { val targetClass = T::class val constructor = targetClass.primaryConstructor!!
val sourceProps = source::class.memberProperties.associateBy { it.name }
val args = constructor.parameters.associateWith { param -> sourceProps[param.name]?.getter?.call(source) ?: if (param.isOptional) null else throw IllegalArgumentException("Missing: ${param.name}") }.filterValues { it != null }
return constructor.callBy(args)}
fun main() { val dto: UserDto = map(entity)}10. Performance Considerations
Phần tiêu đề “10. Performance Considerations”import kotlin.reflect.full.*
data class User(val name: String, val age: Int)
// ❌ Slow: reflection mỗi lần gọifun slowGet(user: User, propName: String): Any? { return user::class.memberProperties .find { it.name == propName } ?.getter?.call(user)}
// ✅ Better: cache property referenceval nameProperty = User::class.memberProperties.find { it.name == "name" }!!
fun fastGet(user: User): String { return nameProperty.get(user) as String}
// ✅ Best: dùng property reference trực tiếpval directRef = User::name
fun fastestGet(user: User): String { return directRef.get(user)}📝 Tóm tắt
Phần tiêu đề “📝 Tóm tắt”| Class | Purpose |
|---|---|
KClass<T> |
Class metadata |
KFunction<R> |
Function reference |
KProperty<R> |
Property reference |
KParameter |
Function/constructor parameter |
KType |
Type information |
Common operations:
::class- get KClass::functionName- function reference::propertyName- property referencecall()- invoke dynamicallycallBy()- invoke with named args
Best practices:
- Cache reflection results khi dùng nhiều lần
- Dùng
isAccessible = truecho private members - Prefer compile-time references khi possible
- Cân nhắc performance overhead