引入
泛型在编译时解决类型问题,但有些场景得在运行时才知道类型是什么——比如序列化框架要遍历结构体字段。Go 的 reflect 包能在运行时检查类型、读取值、甚至修改值,是写通用库的基础。
正文
定义
定义
反射(reflection)是在运行时检查和操作值的类型与值的能力。Go 通过
reflect包提供TypeOf(获取类型信息)和ValueOf(获取值信息)两个核心函数。
语法
import "reflect"
// 获取类型
t := reflect.TypeOf(42)
fmt.Println(t) // int
// 获取值
v := reflect.ValueOf(42)
fmt.Println(v) // 42例子
检查类型
func main() {
var x float64 = 3.14
t := reflect.TypeOf(x)
fmt.Println(t.Kind()) // float64
fmt.Println(t.Name()) // float64
}TypeOf 返回 reflect.Type,Kind() 返回底层类型种类,Name() 返回类型名。
读取值
func main() {
v := reflect.ValueOf("hello")
fmt.Println(v.Kind()) // string
fmt.Println(v.String()) // hello
fmt.Println(v.Len()) // 5
}ValueOf 返回 reflect.Value,根据 Kind() 选对应方法取值:Int()、String()、Float() 等。
修改值(必须传指针)
func main() {
x := 10
v := reflect.ValueOf(&x)
v.Elem().SetInt(20)
fmt.Println(x) // 20
}要修改值,必须传指针的 reflect.Value,再用 Elem() 解引用后调用 Set 系列方法。
遍历结构体字段
type User struct {
Name string
Age int
}
func main() {
u := User{Name: "Alice", Age: 25}
v := reflect.ValueOf(u)
t := v.Type()
for i := 0; i < v.NumField(); i++ {
fmt.Printf("%s: %v\n", t.Field(i).Name, v.Field(i))
}
}反射能遍历结构体的字段名和值,这是 JSON 序列化、ORM 等框架的基础。
调用方法
type Greeter struct{}
func (g Greeter) Hello(name string) string {
return "hello " + name
}
func main() {
g := Greeter{}
v := reflect.ValueOf(g)
method := v.MethodByName("Hello")
args := []reflect.Value{reflect.ValueOf("Bob")}
result := method.Call(args)
fmt.Println(result[0].String()) // hello Bob
}MethodByName 获取方法,Call 调用并传参,返回 []reflect.Value。
判断接口值
func main() {
var i any = []int{1, 2, 3}
v := reflect.ValueOf(i)
fmt.Println(v.Kind()) // slice
fmt.Println(v.Len()) // 3
}反射能从 any 接口值中还原出具体类型和值的信息。
常见写法
| 写法 | 说明 |
|---|---|
reflect.TypeOf(x) | 获取 x 的类型信息 |
reflect.ValueOf(x) | 获取 x 的值信息 |
v.Kind() | 获取值的种类(int、struct、slice 等) |
v.Int() / v.String() | 取具体类型的值 |
v.Elem() | 解引用指针 |
v.SetXxx(val) | 修改值(必须通过指针) |
v.NumField() | 结构体字段数量 |
v.Field(i) | 获取第 i 个字段 |
t.Field(i).Name | 获取字段名 |
v.MethodByName(name) | 获取方法 |
v.Call(args) | 调用方法 |
特点
reflect.TypeOf返回 类型信息,reflect.ValueOf返回值信息- 修改值必须通过指针,用
Elem()解引用后Set Kind()返回的是基础类型种类(reflect.Int、reflect.Struct等)- 反射有性能开销,不要在热路径上滥用
- 反射操作绕过类型检查,错误用法会 panic
- 主要用在框架和库里(JSON、ORM、模板引擎),业务代码少用
理解
反射就是给了 Go 一面镜子,让程序能在运行时看到自己的类型和值。TypeOf 看类型,ValueOf 看值,Elem 解指针,Set 改值。能力很强,但别滥用——框架用它很合理,业务代码里能不用就不用。
引出
反射能动态检查类型,那处理字符串模式匹配呢?接下来看 Go 正则表达式,学习 regexp 包怎么做文本匹配、提取和替换。