Go反射通过reflect.Type和reflect.Value获取变量类型与值;2. 可遍历结构体字段并读取标签,常用于JSON解析和ORM映射。

Go语言的反射(Reflection)机制允许程序在运行时动态获取变量的类型信息和值,并能操作其内容。反射在编码通用库、序列化、ORM框架等场景中非常有用。下面从基础到高级,通过完整示例带你掌握Golang反射的核心用法。
反射的核心是 reflect.Type 和 reflect.Value。使用 reflect.TypeOf 和 reflect.ValueOf 可分别获取变量的类型和值。
示例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x int = 42
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println("类型:", t) // int
fmt.Println("值:", v) // 42
fmt.Println("种类:", t.Kind()) // int
}
注意:Kind() 表示底层数据结构(如 int、struct、slice),而 Type() 是具体类型名。
反射常用于处理结构体,比如解析 JSON 或数据库映射。我们可以遍历字段、读取结构体标签(tag)。
立即学习“go语言免费学习笔记(深入)”;
type User struct {
Name string `json:"name" validate:"required"`
Age int `json:"age"`
}
func inspectStruct(s interface{}) {
t := reflect.TypeOf(s)
v := reflect.ValueOf(s)
if t.Kind() != reflect.Struct {
fmt.Println("输入必须是结构体")
return
}
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
value := v.Field(i)
jsonTag := field.Tag.Get("json")
validateTag := field.Tag.Get("validate")
fmt.Printf("字段: %s, 类型: %s, 值: %v, json标签: %s, validate标签: %s\n",
field.Name, field.Type, value.Interface(), jsonTag, validateTag)
}
}
func main() {
u := User{Name: "Alice", Age: 30}
inspectStruct(u)
}
输出:
字段: Name, 类型: string, 值: Alice, json标签: name, validate标签: required要通过反射修改变量,原始变量必须传入指针,且需使用 Elem() 获取指针指向的值。
func modifyValue(i interface{}) {
v := reflect.ValueOf(i)
// 必须是指针且可设置
if v.Kind() != reflect.Ptr || !v.Elem().CanSet() {
fmt.Println("需要传入可设置的指针")
return
}
e := v.Elem() // 获取指针指向的值
switch e.Kind() {
case reflect.Int:
e.SetInt(100)
case reflect.String:
e.SetString("Modified")
}
}
func main() {
var name string = "Original"
var age int = 20
modifyValue(&name)
modifyValue(&age)
fmt.Println(name) // Modified
fmt.Println(age) // 100
}
反射可以动态调用结构体的方法,前提是方法是导出的(首字母大写)。
type Greeter struct{}
func (g Greeter) SayHello(name string) string {
return "Hello, " + name
}
func (g Greeter) SayGoodbye() {
fmt.Println("Goodbye!")
}
func callMethod(obj interface{}, methodName string, args ...interface{}) []reflect.Value {
v := reflect.ValueOf(obj)
method := v.MethodByName(methodName)
if !method.IsValid() {
panic("方法不存在")
}
in := make([]reflect.Value, len(args))
for i, arg := range args {
in[i] = reflect.ValueOf(arg)
}
return method.Call(in)
}
func main() {
g := Greeter{}
result := callMethod(g, "SayHello", "Bob")
fmt.Println(result[0].String()) // Hello, Bob
callMethod(g, "SayGoodbye") // Goodbye!
}
结合字段标签与反射,我们可以实现一个通用的“字段填充器”,根据 map 数据自动填充结构体。
func fillFromMap(obj interface{}, data map[string]interface{}) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic("obj 必须是指向结构体的指针")
}
v = v.Elem()
t := v.Type()
for i := 0; i < t.NumField(); i++ {
field := t.Field(i)
jsonTag := field.Tag.Get("json")
if key, ok := data[jsonTag]; ok {
f := v.Field(i)
if f.CanSet() {
val := reflect.ValueOf(key)
if f.Type() == val.Type() {
f.Set(val)
}
}
}
}
}
func main() {
u := User{}
data := map[string]interface{}{
"name": "Charlie",
"age": 25,
}
fillFromMap(&u, data)
fmt.Printf("%+v\n", u) // {Name:Charlie Age:25}
}
这个模式在 JSON 反序列化、配置加载等场景非常实用。
反射虽然强大,但有代价:
建议:仅在必要时使用反射,优先考虑接口或代码生成方案(如 stringer、protoc-gen-go)。
基本上就这些。掌握反射,你就拥有了Go语言的“元编程”能力,能写出更灵活的通用代码。
以上就是Golang反射完整示例 从基础到高级应用的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号