开发时会遇到,将json字符串转为struct结构体,这一过程需使用反射。
Go语言是静态编译类语言,定义变量时就已经知道其是什么类型,但是我们在使用过程中有时候会遇到参数时interface{}类型的参数。那么,调用时就可以传递任何类型的参数,那么在函数内部想要知道传递的是什么类型的参数就需要使用反射。
func Println(a ...interface{})(n int,err error){
return FPrintln(os.Stdout,a...)
}
Go语言的反射定义,任何接口由两部分组成:接口的具体类型和具体类型对应的值。
Interface{} 是空接口,可表示任何类型,可把任何类型转换为空接口,它通常用于反射、类型断言、以减少重复代码,简化编程
- reflect.Value:变量的值 ,通过reflect.ValueOf获取
- reflect.Type:变量的类型,通过reflect.TypeOf获取
func main(){
i := 2
ival := reflect.valueOf(i)
itype := reflect.TypeOf(i)
fmt.Println(ival,itype)
}
reflect.Value
reflect 是一个结构体
type value struct{
typ *type
ptr unsafe.Pointer
flag
}
获取原始类型
任意类型对象通过reflect.valueOf转为reflect.Value。那么,reflect.Value通过interface方法转回相对类型对象。
func main(){
i := 3
// int ==> reflect.Value
iv := reflect.ValueOf(i)
//reflect.value ==> int
i1 := iv.interface().(int)
fmt.Println(i1)
}
修改对应的值
通过反射修改在运行时已定义的变量
func main(){
i := 3
ipv := reflect.ValueOf(&i)
ipv.Elem().SetInt(4)
fmt.Println(ipv)
}
reflect.valueOf函数的参数是指针,指针找到对应的内存地址,再通过Elem方法获取指向的值,然后修改其值。
如果修改struct类型的变量值,如果修改?
//1、传递struct结构体指针,获取对应的reflect.Value
//2、Elem方法获取指针指向的值
//3、Field方法获取要修改的字段
//4、Set系列方法修改对应的值
func main(){
type person struct {
Name string //结构体字段必须是公有的,大写
Age int
}
stu := person{
Name: "jasen",
Age: 18,
}
stuRe := reflect.ValueOf(&stu)
stuRe.Elem().Field(1).SetInt(20)
fmt.Println("修改后年龄是", stu.Age)
}
注意:如果要修改 struct 结构体字段值的话,该字段需要是可导出的,而不是私有的,也就是该字段的首字母为大写。
当我讲person结构体中字段改成小写的时候,立马报错,大家可以尝试一下:
panic: reflect: reflect.flag.mustBeAssignable using value obtained using unexported field
获取底层类型
Go语言中,我们可通过关键字type声明很多自定义类型,这里的底层类型就是对应的基础类型(接口、结构体、指针......)
p := person{name:"nanlv", age: 18}
pRef := reflect.ValueOf(&p)
fmt.Println(pRef.Kind())
pval := reflect.ValueOf(p)
fmt.Println(pval.Kind())
//输出结果
// pstr
// struct
Kind方法返回一个Kind类型的值,它是一个常量,具体有哪些值可以通过查看源码
reflect.Type
当我们的场景需要操作变量的类型相关时,可通过使用reflect.TypeOf函数去获取变量类型
reflect.Type 是一个接口
type Type interface {
Implements(u Type) bool //用于判断是否实现了接口 u
AssignableTo(u Type) bool // 用于判断是否可赋值给变量u,即是否可以使用 =
ConvertibleTo(u Type) bool //用于判断是否可以转换成类型u,即是否可以进行类型转换
Comparable() bool // 用于判断该类型是否可比较,即是否可使用关系运算符进行比较
//以下这些方法和Value结构体的功能相同
Kind() Kind
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Elem() Type
Field(i int) StructField
FieldByIndex(index []int) StructField
FieldByName(name string) (StructField, bool)
FieldByNameFunc(match func(string) bool) (StructField, bool)
NumField() int
}
遍历结构体字段和方法
p := person{name:"nanlv", age: 18}
pt := reflect.TypeOf(&p)
// 遍历person字段
for i :=0; i< pt.NumField(); i++ {
fmt.Println("字段:",pt.Field(i).Name)
}
// 遍历 person方法
for i :=0; i< pt.NumMethod(); i++ {
fmt.Println("字段:",pt.pt.Method(i).Name)
}
- FieldByName 获取指定的字段
- MethodByName 获取指定的方法
JSON与Struct互转
Go语言的标准库json包,可以实现此功能
- json.Marshal函数,struct转json
- json.Unmarshal函数,json转struct
经常定义结构体时,字段是公有的,字段名都是大写的,但是我们json字符串是小写,这时候我们就会使用struct tag
// 使用struct tag
type person struct {
Name string `json:"name"` // `bson:"name"`
Age int `json:"age"`
}
// 通过Tag.Get("json")获取对应json的字段名
pt.Field(0).Tag.Get("json")
通过Tag.Get("json")获取对应json的字段名
Struct tag 可当作结构体字段的元数据配置,可使用场景:orm映射,xml转换,生成swagger文档等等