Fundamentos de Reflection
Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy. Lección 83 de 107.
Reflexión permite que un programa examine y manipule su propia estructura en tiempo de ejecución. El paquete reflect de Go proporciona esta capacidad, lo que permite inspeccionar tipos, leer campos de estructuras y llamar a métodos dinámicamente.
Los dos tipos fundamentales de la reflexión son reflect.Type y reflect.Value. Los obtienes mediante reflect.TypeOf() y reflect.ValueOf():
import "reflect"
type Person struct {
Name string
Age int
}
p := Person{Name: "Alice", Age: 30}
t := reflect.TypeOf(p)
v := reflect.ValueOf(p)
fmt.Println(t.Name()) // Person
fmt.Println(t.Kind()) // struct
fmt.Println(v.NumField()) // 2Puedes iterar sobre los campos de una estructura para examinar sus nombres, tipos y valores:
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
fmt.Printf("%s: %v\n", field.Name, value)
}
// Salida:
// Name: Alice
// Age: 30La reflexión es potente, pero conlleva ciertas desventajas: omite la comprobación de tipos en tiempo de compilación, se ejecuta más lentamente que el acceso directo y hace que el código sea más difícil de entender. Úsala con moderación para tareas como la serialización JSON, la asignación ORM o la creación de utilidades genéricas cuando el tipo no se conoce en tiempo de compilación.
Desafío
Fácil¡Construyamos un inspector de estructuras que use la reflexión para examinar cualquier estructura e informar sobre su estructura! Este es un caso de uso práctico de la reflexión: crear utilidades que funcionen con tipos desconocidos en tiempo de compilación.
Organizarás tu código en dos archivos:
inspector.go: Crea tu utilidad de inspección basada en la reflexión.Construye una función
InspectStruct(v any) stringque use el paquetereflectpara examinar cualquier estructura que se le pase. Tu función debe devolver un informe con formato que contenga:- El nombre del tipo de la estructura
- El tipo de la estructura (debería ser "struct")
- El número de campos
- Para cada campo: el nombre del campo, su tipo y su valor actual
El formato de salida debe ser:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Usa
reflect.TypeOf()para obtener información del tipo yreflect.ValueOf()para acceder a los valores de los campos. Recorre los campos usandoNumField(),Field(i)sobre el tipo para los metadatos yField(i)sobre el valor para obtener los valores reales.main.go: Define estructuras de ejemplo y usa tu inspector.Lee un tipo de estructura (
productoemployee) y los valores de sus campos; después, crea la estructura correspondiente e inspecciónala.Para
product: Crea una estructuraProductcon camposName(string),Price(float64) eInStock(bool). Lee el nombre, el precio y el estado de existencias.Para
employee: Crea una estructuraEmployeecon camposID(int),Name(string) yDepartment(string). Lee el ID, el nombre y el departamento.Pasa la estructura creada a
InspectStructe imprime el resultado.
Se proporcionarán las siguientes entradas:
- Línea 1: Tipo de estructura (
productoemployee) - Líneas siguientes: Valores de los campos para ese tipo de estructura
Por ejemplo, dada la siguiente entrada:
product
Laptop
999.99
trueTu salida debe ser:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueY dada la siguiente entrada:
employee
42
Alice Johnson
EngineeringTu salida debe ser:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringY dada la siguiente entrada:
product
Headphones
79.50
falseTu salida debe ser:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseObserva cómo tu inspector funciona con distintos tipos de estructuras sin conocer su estructura de antemano. ¡Este es el poder de la reflexión! La misma función InspectStruct gestiona dinámicamente tanto las estructuras Product como las estructuras Employee.
Pruébalo tú mismo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Estructura Product con los campos Name, Price e InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Estructura Employee con los campos ID, Name y Department
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Leer el tipo de struct
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Leer los campos del product
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
priceStr, _ := reader.ReadString('\n')
priceStr = strings.TrimSpace(priceStr)
price, _ := strconv.ParseFloat(priceStr, 64)
inStockStr, _ := reader.ReadString('\n')
inStockStr = strings.TrimSpace(inStockStr)
inStock, _ := strconv.ParseBool(inStockStr)
// TODO: Crear un struct Product con los valores leídos
// TODO: Llamar a InspectStruct con el product e imprimir el resultado
} else if structType == "employee" {
// Leer los campos del employee
idStr, _ := reader.ReadString('\n')
idStr = strings.TrimSpace(idStr)
id, _ := strconv.Atoi(idStr)
name, _ := reader.ReadString('\n')
name = strings.TrimSpace(name)
department, _ := reader.ReadString('\n')
department = strings.TrimSpace(department)
// TODO: Crear un struct Employee con los valores leídos
// TODO: Llamar a InspectStruct con el employee e imprimir el resultado
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de POO en Go
Archivos externosEspacio de trabajo y módulos de GoPaquetes e importacionesNombres exportados vs. no exportadosIntroducción a la POO en GoStructs como clasesDefinición de métodos en structsReceptores de puntero vs. de valorInicialización de structsFunciones constructorasResumen - Calculadora simple4Interfaces
Introducción a las interfacesImplementación implícitaLa interfaz como contratoInterfaz vacía (any)Type AssertionType SwitchComposición de interfacesInterfaces Stringer y ErrorResumen: Calculadora de figuras7Encapsulamiento
Campos exportados vs. no exportadosEncapsulamiento a nivel de paqueteMétodos Getter y SetterOcultamiento de información en GoResumen - Registros de estudiantes10Genéricos (Go 1.18+)
Introducción a los GenéricosParámetros de tipoRestricciones de tipoStructs genéricosSolución alternativa para métodos genéricosResumen - Colección genérica13Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón Abstract FactoryPatrón ObserverPatrón Strategy2Inmersión profunda en Tipos y Structs
Tipos básicos y compuestosDefiniciones de tipos personalizadosEtiquetas de StructsStructs anónimosStructs anidadosValores cero y por defectoRepaso - Libreta de contactos5Composición sobre herencia
Por qué Go no tiene herenciaConceptos básicos de incrustación de structsPromoción de métodosIncrustación de múltiples structsIncrustación vs AgregaciónShadowing de métodos incrustadosResumen - Jerarquía de empleados8Manejo de Errores y OOP
La interfaz errorTipos de errores personalizadosEnvoltura de errores (fmt.Errorf)Errores centinelaerrors.Is() y errors.As()Panic, Defer y RecoverResumen - Analizador de archivos3Punteros y Memoria
Fundamentos de punteros en GoPunteros a StructsPaso por valor vs referenciaLa función new()Garbage Collection en GoResumen - Constructor de Linked List6Polimorfismo en Go
Polimorfismo mediante interfacesDuck Typing en GoReglas de satisfacción de interfacesColecciones polimórficasInyección de dependenciasResumen - Procesador de pagos9Concurrencia y POO
Fundamentos de GoroutinesChannels y comunicaciónChannels con buffer vs. sin bufferSentencia Selectsync.Mutex y sync.RWMutexsync.WaitGroupDiseño de estructuras Thread-SafeResumen - Worker Pool12Conceptos avanzados de POO
Patrón Functional OptionsPatrón Builder en GoEncadenamiento de métodosAlias de tipos vs. definicionesFundamentos de ReflectionDescripción general de generación de códigoPractica por tu cuenta: Compilador de Go online