Reflection Basics
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 83 of 107.
Reflection allows a program to examine and manipulate its own structure at runtime. Go's reflect package provides this capability, enabling you to inspect types, read struct fields, and call methods dynamically.
The two core types in reflection are reflect.Type and reflect.Value. You obtain them using reflect.TypeOf() and 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()) // 2You can iterate over struct fields to examine their names, types, and values:
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: 30Reflection is powerful but comes with trade-offs: it bypasses compile-time type checking, runs slower than direct access, and makes code harder to understand. Use it sparingly for tasks like JSON marshaling, ORM mapping, or building generic utilities where the type isn't known at compile time.
Challenge
EasyLet's build a struct inspector that uses reflection to examine any struct and report its structure! This is a practical use case for reflection—creating utilities that work with types unknown at compile time.
You'll organize your code across two files:
inspector.go: Create your reflection-based inspection utility.Build a function
InspectStruct(v any) stringthat uses thereflectpackage to examine any struct passed to it. Your function should return a formatted report containing:- The struct's type name
- The struct's kind (should be "struct")
- The number of fields
- For each field: the field name, its type, and its current value
The output format should be:
Type: [TypeName] Kind: struct Fields: [count] - [FieldName] ([FieldType]): [Value] - [FieldName] ([FieldType]): [Value] ...Use
reflect.TypeOf()to get type information andreflect.ValueOf()to access field values. Iterate through fields usingNumField(),Field(i)on the type for metadata, andField(i)on the value for actual values.main.go: Define sample structs and use your inspector.Read a struct type (
productoremployee) and its field values, then create the appropriate struct and inspect it.For
product: Create aProductstruct withName(string),Price(float64), andInStock(bool) fields. Read the name, price, and stock status.For
employee: Create anEmployeestruct withID(int),Name(string), andDepartment(string) fields. Read the ID, name, and department.Pass the created struct to
InspectStructand print the result.
The following inputs will be provided:
- Line 1: Struct type (
productoremployee) - Following lines: Field values for that struct type
For example, given:
product
Laptop
999.99
trueYour output should be:
Type: Product
Kind: struct
Fields: 3
- Name (string): Laptop
- Price (float64): 999.99
- InStock (bool): trueAnd given:
employee
42
Alice Johnson
EngineeringYour output should be:
Type: Employee
Kind: struct
Fields: 3
- ID (int): 42
- Name (string): Alice Johnson
- Department (string): EngineeringAnd given:
product
Headphones
79.50
falseYour output should be:
Type: Product
Kind: struct
Fields: 3
- Name (string): Headphones
- Price (float64): 79.5
- InStock (bool): falseNotice how your inspector works with different struct types without knowing their structure in advance—this is the power of reflection! The same InspectStruct function handles both Product and Employee structs dynamically.
Cheat sheet
The reflect package enables programs to examine and manipulate their own structure at runtime.
The two core types are reflect.Type and reflect.Value, obtained using reflect.TypeOf() and 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()) // 2Iterate over struct fields to examine their names, types, and values:
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: 30Reflection bypasses compile-time type checking, runs slower than direct access, and makes code harder to understand. Use it for tasks like JSON marshaling, ORM mapping, or building generic utilities where the type isn't known at compile time.
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"strconv"
"strings"
)
// Product struct with Name, Price, and InStock fields
type Product struct {
Name string
Price float64
InStock bool
}
// Employee struct with ID, Name, and Department fields
type Employee struct {
ID int
Name string
Department string
}
func main() {
reader := bufio.NewReader(os.Stdin)
// Read the struct type
structType, _ := reader.ReadString('\n')
structType = strings.TrimSpace(structType)
if structType == "product" {
// Read product fields
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: Create a Product struct with the read values
// TODO: Call InspectStruct with the product and print the result
} else if structType == "employee" {
// Read employee fields
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: Create an Employee struct with the read values
// TODO: Call InspectStruct with the employee and print the result
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool12Advanced OOP Concepts
Functional Options PatternBuilder Pattern in GoMethod ChainingType Aliases vs DefinitionsReflection BasicsCode Generation Overview