Paquete reflect en Go: Guía Práctica con Ejemplos
El paquete reflect en Go permite inspeccionar y manipular tipos y valores en tiempo de ejecución. A continuación, se presenta una guía detallada con ejemplos prácticos y casos de uso comunes.
Funciones y Tipos Clave
1. Obtener Tipo y Valor
package main
import (
"fmt"
"reflect"
)
func main() {
x := 42
fmt.Println("Tipo de x:", reflect.TypeOf(x)) // int
fmt.Println("Valor de x:", reflect.ValueOf(x)) // 42
}
2. Modificar Valores (Requiere Direccionabilidad)
func modificarValor() {
y := 10
v := reflect.ValueOf(&y).Elem() // Usar Elem() para obtener el valor direccionable
if v.CanSet() {
v.SetInt(20)
}
fmt.Println("y modificada:", y) // 20
}
3. Crear Valores Dinámicamente
func crearValor() {
t := reflect.TypeOf(0) // Tipo int
zero := reflect.Zero(t) // Valor cero (0)
nuevo := reflect.New(t).Elem() // Nuevo int (0)
nuevo.SetInt(15)
fmt.Println("Valor creado:", nuevo) // 15
}
Casos de Uso Avanzados
1. Inspeccionar una Estructura
type Usuario struct {
Nombre string `json:"nombre"`
Edad int `json:"edad"`
}
func inspeccionarStruct() {
u := Usuario{"Ana", 30}
t := reflect.TypeOf(u)
v := reflect.ValueOf(u)
for i := 0; i < t.NumField(); i++ {
campo := t.Field(i)
valor := v.Field(i)
tag := campo.Tag.Get("json")
fmt.Printf("Campo: %s, Valor: %v, Tag JSON: %s\n", campo.Name, valor, tag)
}
}
// Salida:
// Campo: Nombre, Valor: Ana, Tag JSON: nombre
// Campo: Edad, Valor: 30, Tag JSON: edad
2. Llamar a una Función Dinámicamente
func llamarFuncion() {
// Función a llamar: func Sumar(a, b int) int
suma := func(a, b int) int { return a + b }
v := reflect.ValueOf(suma)
args := []reflect.Value{
reflect.ValueOf(3),
reflect.ValueOf(5),
}
resultado := v.Call(args)
fmt.Println("3 + 5 =", resultado[0].Int()) // 8
}
3. Validar Tipos en Tiempo de Ejecución
func validarTipo(valor any, esperado reflect.Kind) bool {
return reflect.TypeOf(valor).Kind() == esperado
}
func main() {
fmt.Println(validarTipo(42, reflect.Int)) // true
fmt.Println(validarTipo("Hola", reflect.Bool)) // false
}
Mejores Prácticas y Advertencias
- Direccionabilidad:
- Solo los valores obtenidos con
reflect.ValueOf(&x).Elem()son modificables.
x := 5 v := reflect.ValueOf(x) // v.SetInt(10) // Panic: valor no direccionable - Solo los valores obtenidos con
-
Verificar
Kind()Antes de Operar:v := reflect.ValueOf(42) if v.Kind() == reflect.Int { fmt.Println("Es un entero:", v.Int()) } - Rendimiento:
- Evita usar reflection en código crítico para el rendimiento.
- Manejo de Errores:
- Usa
recover()para capturar panics en operaciones inseguras:
defer func() { if r := recover(); r != nil { fmt.Println("Error:", r) } }() - Usa
Ejemplo Integrado: Serialización Genérica
func serializar(valor any) map[string]any {
resultado := make(map[string]any)
v := reflect.ValueOf(valor)
t := reflect.TypeOf(valor)
for i := 0; i < v.NumField(); i++ {
campo := t.Field(i)
clave := campo.Tag.Get("json")
if clave == "" {
clave = campo.Name
}
resultado[clave] = v.Field(i).Interface()
}
return resultado
}
func main() {
u := Usuario{"Carlos", 25}
fmt.Println(serializar(u)) // map[nombre:Carlos edad:25]
}
Conclusión
El paquete reflect es útil para:
- Implementar librerías genéricas (JSON, YAML).
- Validar estructuras dinámicamente.
- Crear sistemas de plugins o configuración flexible.
Recomendaciones:
- Úsalo solo cuando sea necesario (evítalo en código crítico).
- Verifica siempre tipos y direccionabilidad.
- Combínalo con interfaces para mantener seguridad de tipos.
Con estos ejemplos y prácticas, podrás aprovechar el poder de la reflexión en Go manteniendo tu código robusto y eficiente.