Menu
Coddy logo textTech

Fundamentos de Reflection

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy — lição 83 de 107.

A Reflexão permite que um programa examine e manipule sua própria estrutura em tempo de execução. O pacote reflect do Go fornece essa capacidade, permitindo que você inspecione tipos, leia campos de structs e chame métodos dinamicamente.

Os dois tipos principais em reflexão são reflect.Type e reflect.Value. Você os obtém usando reflect.TypeOf() e 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

Você pode iterar sobre os campos de uma struct para examinar seus nomes, tipos e valores:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// Saída:
// Name: Alice
// Age: 30

A Reflection é poderosa, mas traz compensações: ela ignora a verificação de tipos em tempo de compilação, é executada de forma mais lenta do que o acesso direto e torna o código mais difícil de entender. Use-a com moderação para tarefas como marshaling de JSON, mapeamento ORM ou na criação de utilitários genéricos onde o tipo não é conhecido em tempo de compilação.

challenge icon

Desafio

Fácil

Vamos construir um inspetor de structs que usa reflexão para examinar qualquer struct e relatar sua estrutura! Este é um caso de uso prático para reflexão — criar utilitários que funcionam com tipos desconhecidos em tempo de compilação.

Você organizará seu código em dois arquivos:

  • inspector.go: Crie seu utilitário de inspeção baseado em reflexão.

    Construa uma função InspectStruct(v any) string que use o pacote reflect para examinar qualquer struct passada a ela. Sua função deve retornar um relatório formatado contendo:

    • O nome do tipo da struct
    • O kind da struct (deve ser "struct")
    • O número de campos
    • Para cada campo: o nome do campo, seu tipo e seu valor atual

    O formato de saída deve ser:

    Type: [TypeName]
    Kind: struct
    Fields: [count]
    - [FieldName] ([FieldType]): [Value]
    - [FieldName] ([FieldType]): [Value]
    ...

    Use reflect.TypeOf() para obter informações de tipo e reflect.ValueOf() para acessar os valores dos campos. Itere pelos campos usando NumField(), Field(i) no tipo para metadados e Field(i) no valor para os valores reais.

  • main.go: Defina structs de exemplo e use seu inspetor.

    Leia um tipo de struct (product ou employee) e seus valores de campo, então crie a struct apropriada e inspecione-a.

    Para product: Crie uma struct Product com os campos Name (string), Price (float64) e InStock (bool). Leia o nome, o preço e o status do estoque.

    Para employee: Crie uma struct Employee com os campos ID (int), Name (string) e Department (string). Leia o ID, o nome e o departamento.

    Passe a struct criada para InspectStruct e imprima o resultado.

As seguintes entradas serão fornecidas:

  • Linha 1: Tipo da struct (product ou employee)
  • Linhas seguintes: Valores dos campos para esse tipo de struct

Por exemplo, dado:

product
Laptop
999.99
true

Sua saída deve ser:

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

E dado:

employee
42
Alice Johnson
Engineering

Sua saída deve ser:

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

E dado:

product
Headphones
79.50
false

Sua saída deve ser:

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

Observe como seu inspetor funciona com diferentes tipos de struct sem conhecer sua estrutura antecipadamente — este é o poder da reflexão! A mesma função InspectStruct lida com as structs Product e Employee dinamicamente.

Folha de consulta

O pacote reflect permite que os programas examinem e manipulem sua própria estrutura em tempo de execução.

Os dois tipos principais são reflect.Type e reflect.Value, obtidos usando reflect.TypeOf() e 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 os campos da struct para examinar seus nomes, tipos e valores:

for i := 0; i < t.NumField(); i++ {
    field := t.Field(i)
    value := v.Field(i)
    fmt.Printf("%s: %v\n", field.Name, value)
}
// Saída:
// Name: Alice
// Age: 30

A reflexão ignora a verificação de tipos em tempo de compilação, é executada de forma mais lenta do que o acesso direto e torna o código mais difícil de entender. Use-a para tarefas como marshaling JSON, mapeamento ORM ou construção de utilitários genéricos onde o tipo não é conhecido em tempo de compilação.

Experimente você mesmo

package main

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

// Estrutura Product com os campos Name, Price e InStock
type Product struct {
	Name    string
	Price   float64
	InStock bool
}

// Estrutura Employee com os campos ID, Name e Department
type Employee struct {
	ID         int
	Name       string
	Department string
}

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

	// Lê o tipo da estrutura
	structType, _ := reader.ReadString('\n')
	structType = strings.TrimSpace(structType)

	if structType == "product" {
		// Lê os campos do produto
		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: Criar uma estrutura Product com os valores lidos
		// TODO: Chamar InspectStruct com o produto e imprimir o resultado

	} else if structType == "employee" {
		// Lê os campos do funcionário
		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: Criar uma estrutura Employee com os valores lidos
		// TODO: Chamar InspectStruct com o funcionário e imprimir o resultado

	}
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos