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"}

🧠 Test Your Knowledge

How do you get a class reference in Kotlin?