Metaprogramação em Kotlin: como fazer o código escrever código
\n\nImagine 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\nNeste 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\nReflexão em Kotlin: olhando sob o capô
\n\nReflexã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.
Exemplo: suponha que você precise serializar um objeto para JSON sem saber seu tipo antecipadamente. A reflexão permite fazer isso:
\n\nimport 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\nEsta é 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\nFunções inline e tipos reificados
\n\nUma 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\nVeja como é elegante obter o tipo em tempo de execução:
\n\ninline 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\nSem 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.
Anotações: seu DSL pessoal para metadados
\n\nAnotaçõ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