Kotlin Reflection
Inspect and manipulate code structure at runtime
🔍 What is Kotlin Reflection?
Reflection allows your program to examine and modify its own structure at runtime. You can inspect classes, properties, and functions dynamically, making your code more flexible and powerful.
import kotlin.reflect.full.*
class Person(val name: String, var age: Int)
fun main() {
val person = Person("Alice", 25)
val kClass = person::class
println("Class name: ${kClass.simpleName}")
println("Properties: ${kClass.memberProperties.map { it.name }}")
}
// Output: Class name: Person, Properties: [age, name]
Reflection Features
Class References
Get information about classes
val kClass = String::class
println(kClass.simpleName) // "String"
Function References
Reference functions as objects
val function = ::println
function("Hello!") // Calls println
Property References
Access properties dynamically
val prop = Person::name
println(prop.get(person)) // Gets name
Runtime Inspection
Examine code structure at runtime
kClass.memberFunctions.forEach {
println("Function: ${it.name}")
}
🔹 Class Reflection
Inspect class information at runtime:
import kotlin.reflect.full.*
data class Student(
val id: Int,
var name: String,
var grade: Double
) {
fun study() = println("$name is studying")
fun getGradeLevel(): String = when {
grade >= 90 -> "A"
grade >= 80 -> "B"
else -> "C"
}
}
fun main() {
val student = Student(1, "John", 85.5)
val kClass = student::class
// Class information
println("Class: ${kClass.simpleName}")
println("Is data class: ${kClass.isData}")
// Properties
println("\nProperties:")
kClass.memberProperties.forEach { prop ->
println("- ${prop.name}: ${prop.get(student)}")
}
// Functions
println("\nFunctions:")
kClass.memberFunctions.forEach { func ->
println("- ${func.name}")
}
}
Output:
Class: Student
Is data class: true
Properties:
- grade: 85.5
- id: 1
- name: John
Functions:
- getGradeLevel
- study
🔹 Function References
Reference and call functions dynamically:
class Calculator {
fun add(a: Int, b: Int): Int = a + b
fun multiply(a: Int, b: Int): Int = a * b
fun greet(name: String) = println("Hello, $name!")
}
fun topLevelFunction(message: String) {
println("Top level: $message")
}
fun main() {
val calc = Calculator()
// Method references
val addRef = Calculator::add
val multiplyRef = calc::multiply // Bound to instance
println("Add: ${addRef(calc, 5, 3)}") // Need instance
println("Multiply: ${multiplyRef(4, 6)}") // Already bound
// Function reference as parameter
val operations = listOf(Calculator::add, Calculator::multiply)
operations.forEach { operation ->
println("Result: ${operation(calc, 2, 3)}")
}
// Top-level function reference
val topRef = ::topLevelFunction
topRef("Reflection is cool!")
}
Output:
Add: 8
Multiply: 24
Result: 5
Result: 6
Top level: Reflection is cool!
🔹 Property References
Access and modify properties dynamically:
import kotlin.reflect.full.*
class User(var name: String, var email: String, val id: Int) {
var isActive: Boolean = true
}
fun main() {
val user = User("Alice", "[email protected]", 123)
val kClass = user::class
// Get all mutable properties
val mutableProps = kClass.memberProperties.filterIsInstance>()
println("Original values:")
mutableProps.forEach { prop ->
println("${prop.name}: ${prop.get(user)}")
}
// Modify properties dynamically
mutableProps.forEach { prop ->
when (prop.name) {
"name" -> (prop as kotlin.reflect.KMutableProperty1).set(user, "Bob")
"email" -> (prop as kotlin.reflect.KMutableProperty1).set(user, "[email protected]")
"isActive" -> (prop as kotlin.reflect.KMutableProperty1).set(user, false)
}
}
println("\nModified values:")
mutableProps.forEach { prop ->
println("${prop.name}: ${prop.get(user)}")
}
}
Output:
Original values:
email: [email protected]
isActive: true
name: Alice
Modified values:
email: [email protected]
isActive: false
name: Bob
🔹 Practical Example: JSON Serializer
Use reflection to create a simple JSON serializer:
import kotlin.reflect.full.*
data class Product(val id: Int, val name: String, val price: Double, val inStock: Boolean)
fun Any.toJson(): String {
val kClass = this::class
val properties = kClass.memberProperties
val jsonPairs = properties.map { prop ->
val value = prop.get(this)
val jsonValue = when (value) {
is String -> "\"$value\""
is Boolean, is Number -> value.toString()
null -> "null"
else -> "\"$value\""
}
"\"${prop.name}\": $jsonValue"
}
return "{${jsonPairs.joinToString(", ")}}"
}
fun main() {
val product = Product(1, "Laptop", 999.99, true)
val json = product.toJson()
println("Product as JSON:")
println(json)
// Test with different object
data class Person(val name: String, val age: Int, val city: String?)
val person = Person("Alice", 30, null)
println("\nPerson as JSON:")
println(person.toJson())
}
Output:
Product as JSON:
{"id": 1, "inStock": true, "name": "Laptop", "price": 999.99}
Person as JSON:
{"age": 30, "city": null, "name": "Alice"}