Menu
Coddy logo textTech

Fundamentos de Reflection

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 83 de 107.

La 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, permitiéndote inspeccionar tipos, leer campos de estructuras y llamar a métodos de forma dinámica.

Los dos tipos principales en la reflexión son reflect.Type y reflect.Value. Se obtienen utilizando 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())   // 2

Puedes 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: 30

La reflexión es potente pero conlleva compensaciones: evita 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. Úsela con moderación para tareas como el marshaling de JSON, el mapeo ORM o la creación de utilidades genéricas donde el tipo no se conoce en tiempo de compilación.

challenge icon

Desafío

Fácil

¡Vamos a construir un inspector de estructuras que utilice la reflexión para examinar cualquier struct e informar sobre su estructura! Este es un caso de uso práctico para 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 reflexión.

    Crea una función InspectStruct(v any) string que utilice el paquete reflect para examinar cualquier struct que se le pase. Tu función debe devolver un informe formateado que contenga:

    • El nombre del tipo del struct
    • El "kind" del struct (debe 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 de tipo y reflect.ValueOf() para acceder a los valores de los campos. Itera a través de los campos usando NumField(), Field(i) en el tipo para los metadatos, y Field(i) en el valor para los valores reales.

  • main.go: Define structs de ejemplo y usa tu inspector.

    Lee un tipo de struct (product o employee) y sus valores de campo, luego crea el struct apropiado e inspéccionalo.

    Para product: Crea un struct Product con los campos Name (string), Price (float64) e InStock (bool). Lee el nombre, el precio y el estado del stock.

    Para employee: Crea un struct Employee con los campos ID (int), Name (string) y Department (string). Lee el ID, el nombre y el departamento.

    Pasa el struct creado a InspectStruct e imprime el resultado.

Se proporcionarán las siguientes entradas:

  • Línea 1: Tipo de struct (product o employee)
  • Líneas siguientes: Valores de los campos para ese tipo de struct

Por ejemplo, dado:

product
Laptop
999.99
true

Tu salida debería ser:

Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): true

Y dado:

employee
42
Alice Johnson
Engineering

Tu salida debería ser:

Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): Engineering

Y dado:

product
Headphones
79.50
false

Tu salida debería ser:

Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): false

Observa cómo tu inspector funciona con diferentes tipos de struct sin conocer su estructura de antemano; ¡este es el poder de la reflexión! La misma función InspectStruct maneja dinámicamente tanto los structs Product como Employee.

Hoja de referencia

El paquete reflect permite que los programas examinen y manipulen su propia estructura en tiempo de ejecución.

Los dos tipos principales son reflect.Type y reflect.Value, que se obtienen utilizando 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())   // 2

Itere sobre los campos de la 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)
}
// Output:
// Name: Alice
// Age: 30

La reflexión 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. Utilícela para tareas como la serialización JSON (marshaling), el mapeo ORM o la creación de utilidades genéricas donde el tipo no se conoce en tiempo de compilación.

Pruébalo tú mismo

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

// Struct Product con los campos Name, Price e InStock
type Product struct {
	Name    string
	Price   float64
	InStock bool
}

// Struct Employee con los campos ID, Name y Department
type Employee struct {
	ID         int
	Name       string
	Department string
}

func main() {
	reader := bufio.NewReader(os.Stdin)

	// Lee el tipo de struct
	structType, _ := reader.ReadString('\n')
	structType = strings.TrimSpace(structType)

	if structType == "product" {
		// Lee los campos de 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: Crea un struct Product con los valores leídos
		// TODO: Llama a InspectStruct con el product e imprime el resultado

	} else if structType == "employee" {
		// Lee los campos de 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: Crea un struct Employee con los valores leídos
		// TODO: Llama a InspectStruct con el employee e imprime el resultado

	}
}
quiz iconPonte a prueba

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