Sobes.tech
Middle

How do you use reflection in Kotlin?

sobes.tech AI

Answer from AI

Reflection in Kotlin is used for inspecting and manipulating code structures at runtime.

Typical use cases:

  • Annotations: Reading and processing annotations applied to classes, functions, properties.

    // Defining an annotation
    annotation class ApiEndpoint(val path: String)
    
    // Applying the annotation
    @ApiEndpoint("/users")
    class UserController
    
    // Reading the annotation at runtime
    fun processAnnotations() {
        val controllerClass = UserController::class
        val annotation = controllerClass.annotations.find { it is ApiEndpoint } as? ApiEndpoint
        println(annotation?.path) // Will output "/users"
    }
    
  • Dynamic instantiation: Creating class objects by name known only at runtime.

    // Suppose we have a class
    class DynamicClass(val name: String)
    
    fun createInstanceDynamically(className: String) {
        try {
            val kClass = Class.forName(className).kotlin
            val constructor = kClass.constructors.find { it.parameters.size == 1 && it.parameters.first().type.classifier == String::class }
            val instance = constructor?.call("Dynamic object") as? DynamicClass
            println(instance?.name)
        } catch (e: ClassNotFoundException) {
            println("Class $className not found")
        }
    }
    
    // Call
    // createInstanceDynamically("DynamicClass") // Will output "Dynamic object"
    
  • Accessing properties and invoking functions by name: Getting and setting property values, calling object methods when names are known at runtime.

    data class User(var name: String, val age: Int)
    
    fun manipulateObject(obj: Any, propertyName: String, newName: String) {
        val kClass = obj::class
        val property = kClass.members.find { it.name == propertyName } as? kotlin.reflect.KMutableProperty<*>
        if (property != null) {
            try {
                property.setter.call(obj, newName)
                println("Property $propertyName successfully changed to $newName")
            } catch (e: Exception) {
                println("Error changing property: ${e.message}")
            }
        } else {
            println("Property $propertyName not found or is not mutable")
        }
    }
    
    // Call
    // val user = User("Initial Name", 30)
    // manipulateObject(user, "name", "New Name") // Will output "Property name successfully changed to New Name"
    // manipulateObject(user, "age", 31) // Will output "Property age not found or is not mutable"
    
  • Serialization/deserialization: Libraries like Jackson, Gson use reflection for mapping data to objects and back.

  • Testing: For creating mocks or inspecting private members during testing.

  • Frameworks and libraries: Many frameworks (e.g., Spring, Ktor) use reflection for dependency injection, routing, and annotation processing.

It is important to note that using reflection can reduce performance and complicate static code analysis. Therefore, it should be used cautiously and only when other approaches (e.g., static typing, code generation) are not applicable. Kotlin provides reflection APIs in the kotlin.reflect package.