引入

泛型在编译时解决类型问题,但有些场景得在运行时才知道类型是什么——比如序列化框架要遍历结构体字段。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.TypeKind() 返回底层类型种类,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.Intreflect.Struct 等)
  • 反射有性能开销,不要在热路径上滥用
  • 反射操作绕过类型检查,错误用法会 panic
  • 主要用在框架和库里(JSON、ORM、模板引擎),业务代码少用

理解

反射就是给了 Go 一面镜子,让程序能在运行时看到自己的类型和值。TypeOf 看类型,ValueOf 看值,Elem 解指针,Set 改值。能力很强,但别滥用——框架用它很合理,业务代码里能不用就不用。

引出

反射能动态检查类型,那处理字符串模式匹配呢?接下来看 Go 正则表达式,学习 regexp 包怎么做文本匹配、提取和替换。