反射
一、反射的基本介绍
反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
通过反射,可以修改变量的值,可以调用关联的方法。
使用反射,需要import (“reflect”)
二、反射的重要函数和概念
1、reflect.TypeOf(变量名) 获取变量的类型,返回reflect.Type类型
2、reflect.ValueOf(变量名) 获取变量的值,返回reflect.Value类型。reflect.Value是一个结构体类型。通过reflect.Value可以获取到关于该变量的很多信息。
3、变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中会经常使用到。
type Student struct { Name string Age int } func test(b interface{}) { //interface{}转成reflect.Value rVal := reflect.ValueOf(b) //reflect.Value转换成interface{} iVal := rVal.Interface() //interface{}转换成原来的变量类型,使用类型断言 v := iVal.(Student) fmt.Println(v) }
对基本数据类型、interface{}、reflect.Value、结构体类型进行反射的基本操作
package main import ( "fmt" "reflect" ) func reflectTest01(b interface{}) { //通过反射获取传入变量的type、kind、值 //获取到reflect.Type rType := reflect.TypeOf(b) fmt.Println("rType=", rType) //获取到reflect.Value rVal := reflect.ValueOf(b) n := 2 + rVal.Int() fmt.Println("n=", n) fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal) //将rVal转成interface{} iV := rVal.Interface() num := iV.(int) fmt.Println("num=", num) } type Student struct { Name string Age int } func reflectTest02(b interface{}) { rType := reflect.TypeOf(b) fmt.Println("rType=", rType) rVal := reflect.ValueOf(b) iV := rVal.Interface() fmt.Printf("iV=%v iV type=%T\n", iV, iV) stu, ok := iV.(Student) if ok { fmt.Printf("stu.Name=%v\n", stu.Name) } } func main() { var num int = 100 reflectTest01(num) stu := Student{ Name: "tom", Age: 20, } reflectTest02(stu) }
三、反射的注意细节
1、reflect.Value.Kind获取变量的类别,返回的是一个常量。
2、Type和Kind的区别:
Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的。
var num int=100 num的Type是int,Kind也是int
var stu Student stu的Type是pkg1.Student,Kind是struct
3、通过反射可以让变量在interface{}金额reflect.Value之间相互转换。
4、使用反射的方式来获取变量的值并返回对应的类型,要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报panic。
5、通过反射来修改变量,当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法。
package main import ( "fmt" "reflect" ) func testInt(b interface{}) { val := reflect.ValueOf(b) fmt.Printf("val type=%T\n", val) val.Elem().SetInt(110) fmt.Printf("val=%v\n", val) } func main() { var num int = 10 testInt(&num) fmt.Println("num=", num) }
变量var v float64=1.2,使用反射得到它的reflect.Value,然后获取到对应的Type,Kind和值,并将reflect.Value转换成interface{},再将interface{}转换成float64
package main import ( "fmt" "reflect" ) func testFloat(b interface{}) { val := reflect.ValueOf(b) fmt.Printf("val =%v val type=%T\n", val, val) tVal := reflect.TypeOf(b) fmt.Printf("tVal =%v\n", tVal) iT := val.Interface() fNum := iT.(float64) fmt.Printf("fNum=%v\n", fNum) } func main() { var num float64 = 1.2 testFloat(num) }
修改字符串类型的值
package main import ( "fmt" "reflect" ) func main() { var str string = "tom" fs := reflect.ValueOf(&str) fs.Elem().SetString("jack") fmt.Printf("str = %v\n", str) }
使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
package main import ( "fmt" "reflect" ) type Monster struct { Name string `json:"name"` Age int `json:"monster_age"` Score float32 `json:"成绩"` Sex string } func (s Monster) GetSum(n1, n2 int) int { return n1 + n2 } func (s Monster) Set(name string, age int, score float32, sex string) { s.Name = name s.Age = age s.Score = score s.Sex = sex } func (s Monster) Print() { fmt.Println("---start---") fmt.Println(s) fmt.Println("---end---") } func TestStruct(a interface{}) { //获取reflect.Type类型 typ := reflect.TypeOf(a) //获取reflect.Value类型 val := reflect.ValueOf(a) //获取a对应的类别 kd := val.Kind() if kd != reflect.Struct { fmt.Println("expect struct") return } //获取结构体有结果字段 num := val.NumField() fmt.Printf("struct has %d fields\n", num) //结构体的所有字段 for i := 0; i < num; i++ { fmt.Printf("Field %d:值为%v\n", i, val.Field(i)) //获取struct标签,需要通过reflect.Type来获取tag标签的值 tagVal := typ.Field(i).Tag.Get("json") //如果该字段有tag标签及显示,否则就不显示 if tagVal != "" { fmt.Printf("Field %d:tag为%v\n", i, tagVal) } } //获取结构体有多少个方法 numOfMethod := val.NumMethod() fmt.Printf("struct has %d methods\n", numOfMethod) //方法的排序默认是按照函数名排序(ASCII码) //调用结构体的第2个方法 val.Method(1).Call(nil) //调用结构体的第1个方法 var params []reflect.Value params = append(params, reflect.ValueOf(10)) params = append(params, reflect.ValueOf(40)) res := val.Method(0).Call(params) fmt.Println("res=", res[0].Int()) } func main() { var a Monster = Monster{ Name: "黄鼠狼", Age: 400, Score: 30.8, } TestStruct(a) }