Metaprogramação em Kotlin: da reflexão à geração de código

Treinador Online de Python para Iniciantes

Aprenda Python facilmente sem sobrecarga teórica. Resolva tarefas práticas com verificação automática e escreva código diretamente no navegador.

Start Course

Metaprogramação em Kotlin: como fazer o código escrever código

\n\n

Imagine que você está escrevendo código que escreve código. Parece ficção científica? No mundo da metaprogramação em Kotlin isso é realidade. Metaprogramação é uma técnica onde um programa manipula a si mesmo ou a outros programas como dados. Para o desenvolvedor Kotlin, isso é um superpoder que permite reduzir toneladas de código repetitivo, criar frameworks flexíveis e automatizar tarefas rotineiras.

\n\n

Neste artigo, exploraremos técnicas avançadas de metaprogramação em Kotlin: desde a reflexão clássica até a geração moderna de código com KSP. Você aprenderá a usar tipos reificados, funções inline, anotações e muito mais. Vamos lá!

\n\n

Reflexão em Kotlin: olhando sob o capô

\n\n

Reflexão é a base da metaprogramação. Em Kotlin, ela é implementada através da biblioteca kotlin-reflect. Com ela, é possível obter dinamicamente informações sobre classes, funções e propriedades durante a execução.

\n\n

Exemplo: suponha que você precise serializar um objeto para JSON sem saber seu tipo antecipadamente. A reflexão permite fazer isso:

\n\n
import kotlin.reflect.full.memberProperties\n\nfun toJson(obj: Any): String {\n    val sb = StringBuilder("{")\n    val props = obj::class.memberProperties\n    props.forEachIndexed { index, prop ->\n        sb.append("\\"${prop.name}\\": \\"${prop.get(obj)}\\"")\n        if (index < props.size - 1) sb.append(", ")\n    }\n    sb.append("}")\n    return sb.toString()\n}\n\ndata class User(val name: String, val age: Int)\n\nfun main() {\n    val user = User("Alice", 30)\n    println(toJson(user)) // {"name": "Alice", "age": "30"}\n}\n
\n\n

Esta é uma técnica básica, mas poderosa. No entanto, a reflexão tem uma desvantagem: o desempenho. Para sistemas de alta carga, é melhor usar geração de código, que abordaremos mais adiante.

\n\n

Funções inline e tipos reificados

\n\n

Uma das capacidades mais interessantes do Kotlin para metaprogramação são os tipos reificados (reified). Eles funcionam apenas dentro de funções inline e permitem preservar informações sobre o tipo durante a execução, o que normalmente é impossível devido ao apagamento de tipo na JVM.

\n\n

Veja como é elegante obter o tipo em tempo de execução:

\n\n
inline fun <reified T> typeName(): String {\n    return T::class.simpleName ?: "Unknown"\n}\n\nfun main() {\n    println(typeName<String>())   // String\n    println(typeName<List<Int>>()) // List\n}\n
\n\n

Sem o reified, esse truque não funcionaria — o compilador simplesmente apagaria o tipo. Com essa técnica, você pode criar fábricas tipadas, serializadores e até frameworks inteiros. Isso é o tipo reificado em Kotlin em ação.

\n\n

Anotações: seu DSL pessoal para metadados

\n\n

Anotações em Kotlin não são apenas marcadores. Combinadas com reflexão ou geração de código, elas se tornam uma ferramenta poderosa de metaprogramação. Vamos criar nossa própria anotação para validação:

\n\n
@Target(AnnotationTarget.FIELD)\n@Retention(AnnotationRetention.RUNTIME)\nannotation class NotBlank(val message: String = "O campo não deve estar vazio")\n\nfun validate(obj: Any): List<String> {\n    val errors = mutableListOf<String>()\n    obj::class.memberProperties.forEach { prop ->\n        val annotation = prop.findAnnotation<NotBlank>()\n        if (annotation != null) {\n            val value = prop.get(obj) as? String\n            if (value.isNullOrBlank()) {\n                errors.add("${prop.name}: ${annotation.message}")\n            }\n        }\n    }\n    return errors\n}\n\ndata class Form(val name: String, @NotBlank val email: String)\n\nfun main() {\n    val form = Form("João", "")\n    println(validate(form)) // [email: O campo não deve estar vazio]\n}\n                

Blogs

Book Recommendations