Noções Básicas de Reflection
Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 83 de 107.
Reflexão permite que um programa examine e manipule sua própria estrutura em tempo de execução. O pacote reflect do Go oferece esse recurso, permitindo inspecionar tipos, ler campos de structs e chamar métodos dinamicamente.
Os dois tipos fundamentais 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 pelos 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 é poderosa, mas envolve compensações: ignora a verificação de tipos em tempo de compilação, é executada mais lentamente do que o acesso direto e torna o código mais difícil de entender. Use-a com moderação para tarefas como a serialização JSON, o mapeamento de ORM ou a criação de utilitários genéricos quando o tipo não é conhecido em tempo de compilação.
Desafio
FácilVamos criar um inspetor de structs que usa reflexão para examinar qualquer struct e relatar sua estrutura! Este é um caso de uso prático da 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.Crie uma função
InspectStruct(v any) stringque use o pacotereflectpara examinar qualquer struct passado a ela. Sua função deve retornar um relatório formatado contendo:- O nome do tipo da struct
- O tipo 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 da saída deve ser:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Use
reflect.TypeOf()para obter informações sobre o tipo ereflect.ValueOf()para acessar os valores dos campos. Percorra os campos usandoNumField(),Field(i)no tipo para obter metadados eField(i)no valor para obter os valores reais.main.go: Defina structs de exemplo e use seu inspetor.Leia um tipo de struct (
productouemployee) e seus valores de campo, depois 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 desse tipo de struct
Por exemplo, dada a entrada:
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 dada a entrada:
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 dada a entrada:
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. Esse é o poder da reflexão! A mesma função InspectStruct lida dinamicamente com as structs Product e Employee.
Experimente você mesmo
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Struct Product com os campos Name, Price e InStock
type Product struct {
Name string
Price float64
InStock bool
}
// Struct 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 struct
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Lê os campos do 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: Crie uma struct Product com os valores lidos
// TODO: Chame InspectStruct com o product e imprima o resultado
} else if structType == "employee" {
// Lê os campos do 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: Crie uma struct Employee com os valores lidos
// TODO: Chame InspectStruct com o employee e imprima 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 ExternosWorkspace e Módulos do GoPacotes e ImportsNomes Exportados vs. Não ExportadosIntrodução à OOP em GoStructs como ClassesDefinindo Métodos em StructsReceptores de Ponteiro vs. de ValorInicialização de StructsFunções ConstrutorasRecapitulação — Calculadora Simples4Interfaces
Introdução às InterfacesImplementação ImplícitaInterface como ContratoInterface Vazia (any)Asserção de TipoSwitch de TipoComposição de InterfacesInterfaces Stringer e ErrorRecapitulação - Calculadora de Formas7Encapsulamento
Campos Exportados vs. Não ExportadosEncapsulamento no Nível de PacoteMétodos Getters e SettersOcultação de Informações em GoRecapitulação - Registros de Alunos10Genéricos (Go 1.18+)
Introdução aos genéricosParâmetros de tipoRestrições de tipoStructs genéricasAlternativa para métodos genéricosRecapitulação - Coleção genérica13Padrões de Projeto — Parte 1
Introdução aos Padrões de ProjetoPadrão SingletonPadrão FactoryPadrão Abstract FactoryPadrão ObserverPadrão Strategy2Tipos e Structs em Profundidade
Tipos Básicos e CompostosDefinições de Tipos PersonalizadosTags de StructStructs AnônimasStructs AninhadasValores Zero e PadrõesRecapitulação - Livro de Contatos5Composição em vez de herança
Por que Go não tem herançaNoções básicas de incorporação de structsPromoção de métodosIncorporação de várias structsIncorporação vs agregaçãoOcultação de métodos incorporadosRecapitulação - Hierarquia de funcionários8Tratamento de Erros e OOP
A interface errorTipos de Erro PersonalizadosEncapsulamento de Erros (fmt.Errorf)Erros Sentinelaerrors.Is() e errors.As()Panic, Defer e RecoverRecapitulação - Analisador de Arquivos3Ponteiros e Memória
Fundamentos de Ponteiros em GoPonteiros para StructsPassagem por Valor vs. ReferênciaA Função new()Coleta de Lixo em GoRecapitulação - Construtor de Lista Ligada6Polimorfismo em Go
Polimorfismo via InterfacesDuck Typing em GoRegras de Satisfação de InterfacesColeções PolimórficasInjeção de DependênciasRecapitulação - Processador de Pagamentos9Concorrência e OOP
Fundamentos de GoroutinesCanais e comunicaçãoCanais com e sem bufferInstrução selectsync.Mutex e sync.RWMutexsync.WaitGroupDesign de structs thread-safeRevisão - Worker Pool12Conceitos Avançados de OOP
Padrão de Opções FuncionaisPadrão Builder em GoEncadeamento de MétodosAliases de Tipos vs. DefiniçõesNoções Básicas de ReflectionVisão Geral da Geração de CódigoPratique por conta própria: Compilador de Go online