Reflexão e introspecção em Kotlin: como olhar dentro do seu código
Imagine que você é um programador que escreve código que se analisa. Parece ficção científica? No mundo Kotlin, isso é realidade. Reflexão e introspecção são as ferramentas que transformam seu código de um simples executor em um verdadeiro meta-programador. Se você já quis obter informações sobre classes dinamicamente, chamar métodos pelo nome ou trabalhar com anotações em tempo de execução, este artigo é para você.
Vamos explorar como o Kotlin, sendo uma linguagem que roda sobre a JVM, utiliza a reflexão e como, com exemplos simples, você pode adicionar magia aos seus projetos. Vamos nessa!
O que são reflexão e introspecção? A diferença que você precisa saber
Antes de mergulharmos no código, vamos esclarecer os termos. Esses conceitos são frequentemente confundidos, mas há uma diferença sutil entre eles.
- Introspecção — é a capacidade do programa de "olhar para si mesmo". Você pode obter informações sobre a estrutura das classes: quais são suas propriedades, métodos, construtores. É como abrir o capô de um carro e ver o que há dentro.
- Reflexão — é algo mais poderoso. Você não apenas observa, mas também altera o comportamento do programa durante a execução. Pode chamar métodos que não conhecia em tempo de compilação, criar objetos a partir do nome da classe em formato de string.
Em Kotlin, a reflexão está intimamente ligada à reflexão da JVM, mas possui suas próprias APIs específicas do Kotlin (por exemplo, KClass, KFunction). E sim, para trabalhar com reflexão, você precisará adicionar uma dependência no build.gradle.kts:
dependencies { implementation("org.jetbrains.kotlin:kotlin-reflect:1.9.22")}Sem ela, você só poderá usar recursos básicos, como javaClass. Mas para a experiência completa do Kotlin, instale a biblioteca.
KClass: sua principal ferramenta de introspecção
Em Java, estamos acostumados com Class<?>. Em Kotlin, seu equivalente é KClass. Este é o ponto de entrada para o mundo da reflexão. Você pode obter um KClass de duas maneiras:
// Forma 1: através do objeto da classeval myClass: KClass<String> = String::class
// Forma 2: através de uma instânciaval str = "Olá, mundo!"val classFromInstance: KClass<out String> = str::classAgora você tem acesso a várias informações: nome da classe, pacote, construtores, propriedades, funções. Vejamos um exemplo:
data class User(val name: String, val age: Int)
fun main() { val kClass = User::class println("Nome da classe: ${kClass.simpleName}") // User println("Pacote: ${kClass.qualifiedName}") // com.example.User println("Número de propriedades: ${kClass.memberProperties.size}") // 2 (name, age) kClass.memberProperties.forEach { prop -> println("Propriedade: ${prop.name}, tipo: ${prop.returnType}") }}Isso é pura introspecção em Kotlin — estamos apenas observando a estrutura. Nenhuma alteração de comportamento.
Obtendo informações sobre funções e construtores
Às vezes, você precisa saber quais métodos uma classe possui, ou até mesmo chamá-los. Para isso, usamos KFunction.
class Calculator { fun add(a: Int, b: Int): Int = a + b fun multiply(a: Int, b: Int): Int = a * b}
fun main() { val calcClass = Calculator::class println("Funções da classe Calculator:") calcClass.memberFunctions.forEach { func -> println(" - ${func.name} (parâmetros: ${func.parameters})") }}A saída será algo como:
Funções da classe Calculator: - add (parâmetros: [a, b]) - multiply (parâmetros: [a, b])Reflexão em ação: chamando métodos e criando objetos
Agora a parte mais interessante — a reflexão. Suponha que você tenha uma string com o nome de um método,