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()) // 2Você 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: 30A 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.
Desafio
FácilVamos 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) stringque use o pacotereflectpara 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 ereflect.ValueOf()para acessar os valores dos campos. Itere pelos campos usandoNumField(),Field(i)no tipo para metadados eField(i)no valor para os valores reais.main.go: Defina structs de exemplo e use seu inspetor.Leia um tipo de struct (
productouemployee) e seus valores de campo, então crie a struct apropriada e inspecione-a.Para
product: Crie uma structProductcom os camposName(string),Price(float64) eInStock(bool). Leia o nome, o preço e o status do estoque.Para
employee: Crie uma structEmployeecom os camposID(int),Name(string) eDepartment(string). Leia o ID, o nome e o departamento.Passe a struct criada para
InspectStructe imprima o resultado.
As seguintes entradas serão fornecidas:
- Linha 1: Tipo da struct (
productouemployee) - Linhas seguintes: Valores dos campos para esse tipo de struct
Por exemplo, dado:
product
Laptop
999.99
trueSua saída deve ser:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueE dado:
employee
42
Alice Johnson
EngineeringSua saída deve ser:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringE dado:
product
Headphones
79.50
falseSua saída deve ser:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseObserve 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()) // 2Itere 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: 30A 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
}
}
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
1Fundamentos de OOP em Go
Arquivos ExternosGo Workspace e MódulosPacotes e ImportaçõesNomes Exportados vs Não ExportadosIntrodução a OOP em GoStructs como ClassesDefinindo Métodos em StructsRecebedores de Ponteiro vs ValorInicialização de StructsFunções ConstrutorasRecapitulação - Calculadora Simples4Interfaces
Introdução a InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Type AssertionType SwitchComposição de InterfacesInterfaces Stringer & ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs Não ExportadosEncapsulamento em Nível de PacoteMétodos Getter e SetterOcultação de Informação em GoRecapitulação - Registros de Estudantes10Generics (Go 1.18+)
Introdução a GenericsParâmetros de TipoRestrições de TipoStructs GenéricasSolução Alternativa para Métodos GenéricosRecapitulação - Coleção Genérica2Mergulho Profundo em Tipos e Structs
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosStruct TagsStructs AnônimasStructs AninhadasZero Values e PadrõesRecapitulação - Agenda de Contatos5Composição em vez de Herança
Por que Go não possui HerançaFundamentos de Incorporação de StructsPromoção de MétodosIncorporando Múltiplas StructsIncorporação vs AgregaçãoShadowing de Métodos IncorporadosRecapitulação - Hierarquia de Funcionários8Tratamento de Erros e OOP
A Interface errorTipos de Erro PersonalizadosError Wrapping (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - File Parser3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs ReferênciaA Função new()Garbage Collection em GoRecapitulação - Construtor de Lista Encadeada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfaceColeções PolimórficasInjeção de DependênciaRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e ComunicaçãoCanais com Buffer vs. Sem BufferInstrução Selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de Structs Thread-SafeRecapitulação - Worker Pool12Conceitos Avançados de OOP
Padrão Functional OptionsPadrão Builder em GoEncadeamento de MétodosType Aliases vs DefiniçõesFundamentos de ReflectionVisão Geral de Geração de Código