第 12 章:反射与泛型
jerry北京市2026年5月9日Go 20 次阅读 约 12 分钟

理解 reflect 包的核心 API、Go 1.18 泛型语法,以及它们的实际应用场景。
12.1 反射基础
反射允许程序在运行时检查和操作类型信息:
import "reflect"
x := 42
t := reflect.TypeOf(x) // reflect.Type
v := reflect.ValueOf(x) // reflect.Value
fmt.Println(t) // int
fmt.Println(t.Kind()) // int
fmt.Println(v) // 42
fmt.Println(v.Int()) // 42
reflect.Type 和 reflect.Value
type User struct {
Name string `json:"name" validate:"required"`
Age int `json:"age"`
}
u := User{Name: "Alice", Age: 30}
t := reflect.TypeOf(u)
v := reflect.ValueOf(u)
// 遍历字段
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
tag := field.Tag.Get("json")
fmt.Printf("字段: %s, 类型: %s, 值: %v, Tag: %s\n",
field.Name, field.Type, value, tag)
}
// 字段: Name, 类型: string, 值: Alice, Tag: name
// 字段: Age, 类型: int, 值: 30, Tag: age
通过反射修改值
x := 42
v := reflect.ValueOf(&x).Elem() // 必须传指针才能修改
if v.CanSet() {
v.SetInt(100)
}
fmt.Println(x) // 100
// 修改结构体字段
u := &User{Name: "Alice", Age: 30}
v = reflect.ValueOf(u).Elem()
nameField := v.FieldByName("Name")
if nameField.CanSet() {
nameField.SetString("Bob")
}
fmt.Println(u.Name) // Bob
反射调用方法
type Calculator struct{}
func (c Calculator) Add(a, b int) int {
return a + b
}
calc := Calculator{}
v := reflect.ValueOf(calc)
method := v.MethodByName("Add")
args := []reflect.Value{
reflect.ValueOf(3),
reflect.ValueOf(4),
}
result := method.Call(args)
fmt.Println(result[0].Int()) // 7
反射的性能代价
反射比直接调用慢 10-100 倍,应该谨慎使用:
- 适合:框架、ORM、序列化库等基础设施代码
- 不适合:性能敏感的热路径
12.2 泛型(Go 1.18+)
基本语法
// 泛型函数
func Max[T int | float64 | string](a, b T) T {
if a > b {
return a
}
return b
}
fmt.Println(Max(3, 5)) // 5
fmt.Println(Max(3.14, 2.71)) // 3.14
fmt.Println(Max("a", "b")) // b
类型约束
// 定义约束接口
type Number interface {
int | int8 | int16 | int32 | int64 |
float32 | float64
}
func Sum[T Number](nums []T) T {
var total T
for _, n := range nums {
total += n
}
return total
}
// 使用 ~ 支持底层类型
type MyInt int
type Addable interface {
~int | ~float64 // ~ 表示底层类型是 int 或 float64 的都可以
}
func Add[T Addable](a, b T) T {
return a + b
}
var x MyInt = 1
var y MyInt = 2
fmt.Println(Add(x, y)) // 3
内置约束
import "golang.org/x/exp/constraints"
// comparable:支持 == 和 != 的类型
func Contains[T comparable](slice []T, target T) bool {
for _, v := range slice {
if v == target {
return true
}
}
return false
}
// any:等价于 interface{}
func PrintAll[T any](items []T) {
for _, item := range items {
fmt.Println(item)
}
}
泛型结构体
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
// 使用
intStack := Stack[int]{}
intStack.Push(1)
intStack.Push(2)
v, _ := intStack.Pop() // 2
strStack := Stack[string]{}
strStack.Push("hello")
泛型实际应用
// 通用的 Map 函数
func Map[T any, U any](slice []T, fn func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = fn(v)
}
return result
}
nums := []int{1, 2, 3, 4}
strs := Map(nums, func(n int) string {
return fmt.Sprintf("第%d个", n)
})
// ["第1个", "第2个", "第3个", "第4个"]
// 通用的 Filter 函数
func Filter[T any](slice []T, predicate func(T) bool) []T {
var result []T
for _, v := range slice {
if predicate(v) {
result = append(result, v)
}
}
return result
}
evens := Filter(nums, func(n int) bool { return n%2 == 0 })
// [2, 4]
12.3 面试要点
-
反射的三大法则?
- 从接口值到反射对象(TypeOf/ValueOf)
- 从反射对象到接口值(Interface())
- 要修改反射对象,值必须可设置(CanSet,需要传指针)
-
反射的性能影响?
- 比直接调用慢 10-100 倍
- 会导致额外的内存分配
- 应避免在热路径中使用
-
泛型中
~的作用?- 匹配底层类型,如
~int匹配所有底层类型为 int 的类型
- 匹配底层类型,如
-
comparable约束包含哪些类型?- 所有支持
==和!=的类型 - 包括基本类型、指针、channel、数组(元素可比较时)、结构体(字段都可比较时)
- 所有支持
-
什么时候用泛型,什么时候用接口?
- 泛型:类型安全的容器、通用算法
- 接口:行为抽象、多态
练习
- 使用反射实现一个简单的结构体字段验证器(基于 tag)
- 实现泛型版本的
Map、Filter、Reduce函数 - 实现一个泛型的
Set数据结构 - 使用反射实现一个简单的 JSON 序列化函数
评论
登录 后发表评论
暂无评论