Metaprogramación en Kotlin: de la reflexión a la generación de código

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Metaprogramación en Kotlin: cómo hacer que el código escriba código

\n\n

Imagina que escribes código que escribe código. ¿Suena a ciencia ficción? En el mundo de la metaprogramación en Kotlin esto es una realidad. La metaprogramación es una técnica en la que un programa se manipula a sí mismo o a otros programas como datos. Para un desarrollador de Kotlin, esto es un superpoder que permite reducir toneladas de código repetitivo, crear frameworks flexibles y automatizar tareas rutinarias.

\n\n

En este artículo, exploraremos las técnicas avanzadas de metaprogramación en Kotlin: desde la reflexión clásica hasta la generación de código moderna con KSP. Aprenderás a usar tipos reificados, funciones inline, anotaciones y mucho más. ¡Vamos allá!

\n\n

Reflexión en Kotlin: mirando bajo el capó

\n\n

La reflexión es la base de la metaprogramación. En Kotlin se implementa a través de la biblioteca kotlin-reflect. Con ella, puedes obtener dinámicamente información sobre clases, funciones y propiedades durante la ejecución.

\n\n

Ejemplo: supongamos que necesitas serializar un objeto a JSON sin conocer su tipo de antemano. La reflexión te permite hacerlo:

\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 es una técnica básica pero poderosa. Sin embargo, la reflexión tiene una desventaja: el rendimiento. Para sistemas de alta carga, es mejor usar la generación de código, de la que hablaremos más adelante.

\n\n

Funciones inline y tipos reificados

\n\n

Una de las características más interesantes de Kotlin para la metaprogramación son los tipos reificados (reified). Solo funcionan dentro de funciones inline y permiten conservar la información del tipo durante la ejecución, algo que normalmente es imposible debido al borrado de tipos en la JVM.

\n\n

Observa cómo se puede obtener el tipo en tiempo de ejecución de forma elegante:

\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

Sin reified, este truco no funcionaría: el compilador simplemente borraría el tipo. Con esta técnica, puedes crear fábricas tipadas, serializadores e incluso frameworks completos. Esto es el tipo reificado en Kotlin en acción.

\n\n

Anotaciones: tu propio DSL para metadatos

\n\n

Las anotaciones en Kotlin no son solo marcadores. Combinadas con reflexión o generación de código, se convierten en una potente herramienta de metaprogramación. Creemos nuestra propia anotación para validación:

\n\n
@Target(AnnotationTarget.FIELD)\n@Retention(AnnotationRetention.RUNTIME)\nannotation class NotBlank(val message: String = "El campo no debe estar vacío")\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("Iván", "")\n    println(validate(form)) // [email: El campo no debe estar vacío]\n}\n                

Blogs

Book Recommendations