(1).对于结构体的序列化和反序列化,看一段代码
- package main
-
- import(
- "fmt"
- "encoding/josn"
- )
-
- type Monster struct {
- Name string `json:"monsterName"`
- Age int `json:"monsterAge"`
- Sal flotat64 `json:"monsterSal"`
- Sex string `json:"monsterSex"`
- }
-
- func main() {
- m := Monster {
- Name : "狗子",
- Age : 12,
- Sal : 22.33,
- Sex : "公",
- }
- data, _ := json.Marsha1(m)
- fmt.Println("json results: ", string(data))
- }
输出结果:
json results: {"monsterName": "狗子", Age: 12, Sal : 22.33, Sex : "公"}
思考:
为什么序列化后,key-val的key值是结构体Tag的值,而不是字段的名称,比如:不是Name而是"monsterName ": "狗子"
引出反射
要求如下:
(1).定义了两个匿名函数
test1 = func(v1 int, v2 int) { t.Log(v1, v2) } test2 = func(v1 int, v2 int, s string) { t.Log(v1, v2, s) }(2).定义一个适配器函数用作统一处理接口,其大致结构如下:
bridge := func(call interface{}, args ...interface{}){ //内容 } //实现调研test1对应的函数 bridge(test1, 1, 2) //实现调研test2对应的函数 bridge(test2, 1, 2, "test2")
(1).反射可以在运行时动态获取变量的各种信息,比如变量的类型(type ) ,类别(kind)
(2).如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
(3).通过反射,可以修改变量的值,可以调用关联的方法
(4).使用反射,需要加import ("refect")
(5).示意图,如下:
(1).不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射:例如以下这种桥接模式:
bridge(funcPtr interface{}, args ...interface{})
第一个参数 funcPtr 以接口的形式传入函数指针,函数参数 args 以可变参数的形式传入,bridge函数中可以用反射来动态执行funcPtr
(2).对结构体序列化,如果结构体有指定的Tag,也会使用到反射生成对应的字符串
- package main
-
- import(
- "fmt"
- "encoding/josn"
- )
-
- type Monster struct {
- Name string `json:"monsterName"`
- Age int `json:"monsterAge"`
- Sal flotat64 `json:"monsterSal"`
- Sex string `json:"monsterSex"`
- }
-
- func main() {
- m := Monster {
- Name : "狗子",
- Age : 12,
- Sal : 22.33,
- Sex : "公",
- }
- data, _ := json.Marsha1(m)
- fmt.Println("json results: ", string(data))
- }
(1).reflect.TypeOf(变量名),获取变量的类型,返回 reflect.Type类型 )
(2).reflect.ValueOf(变最名),获取变量的值,返回 reflectValue 类型,reflectValue 是一个结构体类型。 【看文档】 ,通过reflect.Value,可以获取到关于该变量的很多信息
(3). 变量,interface{},reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到,示意图如下:
(1).演示对(基本数据类型、 interface{}、 reflect.Value)进行反射的基本操作代码演示
(2).演示对(结构体类型、 interface{}、 reflect.Value )进行反射的基本操作
- package main
-
- import (
- "fmt"
- "reflect"
- )
-
- //演示反射
- func reflectTest(b interface{}) {
- //通过反射获取传入值的type,kind,值
- //1.先获取到reflect.Type
- rTyp := reflect.TypeOf(b)
- fmt.Println("rTyp=", rTyp) //rTyp= int
-
- //2.获取reflect.ValueOf()
- rVal := reflect.ValueOf(b)
-
- //运算
- n := 12 + rVal.Int()
- n2 := rVal.Float() // panic
- fmt.Println("n=", n) // n=112
- fmt.Println("n2=", n2) // panic: reflect: call of reflect.Value.Float on int Value
-
- fmt.Printf("rVal=%v, rVal type=%T\n", rVal, rVal)//rVal=100, rVal type=reflect.Value
-
- //下面将rVal转成interface{}
- iV := rVal.Interface()
- //将interface{}通过类型断言转成需要的类型
- num := iV.(int) //使用类型断言
- fmt.Println("num=", num)
-
- //3.获取变量对应的Kind(Kind代表Type类型值表示的具体分类。零值表示非法分类。)
- //rVal.Kind()
- kind := rVal.Kind()
- //rTyp.Kind()
- kind2 := rTyp.Kind()
- fmt.Printf("kind = %v, kind2 = %v\n", kind, kind2)
- }
- //演示结构体的反射
- func reflectTest2(b interface{}) {
- //通过反射获取传入值的type,kind,值
- //1.先获取到reflect.Type
- rTyp := reflect.TypeOf(b)
- fmt.Println("rTyp=", rTyp)
-
- //2.获取reflect.ValueOf()
- rVal := reflect.ValueOf(b)
-
- //下面将rVal转成interface{}
- iV := rVal.Interface()
- fmt.Printf("iV=%v, iV type=%T\n", iV, iV)
- //将interface{}通过类型断言转成需要的类型
- //使用一个简单的带检测的类型断言, 还可以使用switch封装成一个方法来判断
- stu, ok := iV.(Student)
- if ok {
- fmt.Printf("stu.Name=%v, stu.Age=%v\n", stu.Name, stu.Age)
- }
-
- //3.获取变量对应的Kind(Kind代表Type类型值表示的具体分类。零值表示非法分类。)
- //rVal.Kind()
- kind := rVal.Kind()
- //rTyp.Kind()
- kind2 := rTyp.Kind()
- fmt.Printf("kind = %v, kind2 = %v\n", kind, kind2)
- }
-
- type Student struct {
- Name string
- Age int
- }
-
- func main() {
- //编写一个案例
- //演示对(基本数据类型,interface{},reflect.Value)进行反射的基本操作
- //1.先定义一个int
- var num int = 100
- reflectTest(num)
-
- //2.定义一个Student实例
- stu := Student{
- Name : "tom",
- Age : 12,
- }
- reflectTest2(stu)
- }
(1).reflect.Value.Kind ,获取变量的类别,返回的是一个常量
(2).Type和Kind的区别
Type是类型, Kind 是类别, Type 和 Kind 可能是相同的,也可能是不同的
比如: var num int = 10 , num 的Type是 int , Kind 也是 int
比如:var stu Student, stu的Type是pkg1.Student,Kind是 struct
(3).通过反射可以在让变量在 interface{}和 Reflect.value 之间相互转换,看下是如何在代码中体现
变量<===> interface{} <===> reflect.Value
- //通过反射获取传入值的type,kind,值
- //1.先获取到reflect.Type
- rTyp := reflect.TypeOf(b)
- fmt.Println("rTyp=", rTyp)
-
- //2.获取reflect.ValueOf()
- rVal := reflect.ValueOf(b)
-
- //下面将rVal转成interface{}
- iV := rVal.Interface()
- fmt.Printf("iV=%v, iV type=%T\n", iV, iV)
- //将interface{}通过类型断言转成需要的类型
- //使用一个简单的带检测的类型断言, 还可以使用switch封装成一个方法来判断
- stu, ok := iV.(Student)
- if ok {
- fmt.Printf("stu.Name=%v, stu.Age=%v\n", stu.Name, stu.Age)
- }
(4).使用反射的方式来获取变量的值 (并返回对应的类型),要求数据类型匹配,比如 x 是 int , 那么就应该使用reflect.VaIue (x).Int(),而不能使用其它的,否则报 panic
- //运算
- n := 12 + rVal.Int()
- n2 := rVal.Float() // panic
- fmt.Println("n=", n) // n=112
- fmt.Println("n2=", n2) // panic: reflect: call of reflect.Value.Float on int Value
(5).通过反射的来修改变量,注意当使用SetXxx 方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到\reflect.Value.Elem()方法
- package main
-
- import (
- "fmt"
- "reflect"
- )
-
- func reflectTest(b interface{}) {
- //通过反射获取传入值的type,kind,值
- //1.先获取到reflect.Type
- rTyp := reflect.TypeOf(b)
- fmt.Println("rTyp=", rTyp) //rTyp= *int
-
- //2.获取reflect.ValueOf()
- rVal := reflect.ValueOf(b)
- //rVal当前的Kind值(是一个指针)
- fmt.Printf("rVal kind = %v\n", rVal) //rVal kind = 0xc0000aa058
- //3.rVal: rval.Elem() 返回rVal具体指针指向的那个值
- //Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
- rVal.Elem().SetInt(20)
- }
- func main() {
- var num int = 100
- reflectTest(&num)
- fmt.Printf("num = %v\n", num)//num = 20
- }
- package main
-
- import(
- "fmt"
- "reflect"
- )
-
- func main() {
- //看看下面是否正确
- var str string = "tom" //ok
- rf := reflect.ValueOf(str) //ok :rf => string
- rf.SetString("mary") //error
- fmt.Println("%v", str)
- }
- package main
-
- import(
- "fmt"
- "reflect"
- )
-
- func main() {
- //正确写法
- var str string = "tom" //ok
- rf := reflect.ValueOf(&str) //ok :rf => string
- rf.Elem().SetString("mary") //ok: str => mary
- fmt.Println("%v", str)//mary
- }
(1).使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
- package main
-
- import(
- "fmt"
- "reflect"
- )
-
- //定义一个Monster的结构体方法
- type Monster struct {
- Name string `json:"name"`
- Age int `json:"monster_age"`
- Score float32
- Sex string
- }
- //方法:显示s的值
- func (s Monster) Print() {
- fmt.Println("---start---")
- fmt.Println(s)
- fmt.Println("---end---")
- }
- //方法:返回两个数的和
- func (s Monster) GetSum(n1 int, n2 int) int {
- return n1 + n2
- }
- //方法:接收4个值,给Monster赋值
- 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 TestStruct(a interface{}) {
- //获取reflect.Type 类型
- typ := reflect.TypeOf(a)
- //获取reflect.Value 类型
- val := reflect.ValueOf(a)
- //获取a对应的类别
- kind := val.Kind()
- //如果传入的a不是struct类别,就退出
- if kind != reflect.Struct {
- fmt.Println("except struct")
- return
- }
- //获取该结构体有几个字段
- num := val.NumField()
- fmt.Printf("struct has %d filed\n", num) // 4
- //遍历结构体所有字段
- for i := 0; i < num; i ++ {
- fmt.Printf("Filed %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)
- }
- }
-
- //获取该结构体有多少个方法
- numMethod := val.NumMethod()
- fmt.Printf("struct has %d method\n", numMethod)
- //var parmas [] reflect.Value
- //方法的默认排序是按照函数名来排序的(ASCII码)
- val.Method(1).Call(nil) //获取到第二个方法,并调用它
-
- //调用结构体的第一个方法Method(0)
- var params []reflect.Value //声明了 []reflect.Value
- params = append(params, reflect.ValueOf(10))
- params = append(params, reflect.ValueOf(20))
- res := val.Method(0).Call(params) //传入的参数是 []reflect.Value,返回的是[]reflect.Value
- fmt.Printf("res = %v\n", res[0].Int())//返回结果,返回的是[]reflect.Value
- }
- func main() {
- //定义一个Monster实例
- var s Monster = Monster{
- Name: "猫",
- Age : 12,
- Score : 10.11,
- Sex : "公的",
- }
- //将monster实例传递给TestStruct函数
- TestStruct(s)
- }
(2).使用反射的方式来获取结构体的 tag 标签,遍历字段的值,修改字段值,调用结构体方法(要求:通过传递地址的方式完成,在前面案例上修改即可)
反射的struct tag核心代码:
tag := typ.Elem().Field(0).TagGet("json")
(3).定义两个函数test1和test2,定义一个适配器函数,用作统一接口处理
- //定义两个函数
- test1 = func(v1 int, v2 int) {
- t.Log(v1, v2)
- }
-
- test2 = func(v1 int, v2 int, s string) {
- t.Log(v1, v2, s)
- }
-
- //定义一个适配器函数,用作统一处理接口
- bridge := func(call interface{}, args ...interface{}){
- //内容
- }
-
- //实现调研test1对应的函数
- bridge(test1, 1, 2)
- //实现调研test2对应的函数
- bridge(test2, 1, 2, "test2")
(4).使用反射操作任意结构体类型
- type User struct {
- UserId string
- Name string
- }
-
- func TestRelectStruct (t *testing.T) {
- var (
- model *User
- sv reflect.Value
- )
- model := &User{}
- sv = reflect.ValueOf(model)
- t.Log("reflect.ValueOf", sv.Kind().String())
- sv = sv.Elem()
- t.Log("reflect.ValueOf.Elem", sv.Kind().String())
- sv.FieldByName("UserId").SetString("123456")
- sv.FieldByName("Name").SetString("nickname")
- t.Log("model", model)
- }
(5).使用反射创建并操作结构体
- package test
-
- import (
- "testing"
- "reflect"
- )
-
- type User struct {
- UserId string
- Name string
- }
-
- func TestRelectStruct (t *testing.T) {
- var (
- model *User
- st reflect.Type
- elem reflect.Value
- )
-
- st = reflect.TypeOf(model) //获取类型 *User
- t.Log("reflect.TypeOf", st.Kind().String()) // ptr
- st = st.Elem() //st指向的类型
- t.Log("reflect.TypeOf.Elem", st.Kind().String()) //struct
- elem = reflect.New(st) // New返回一个Value类型值,该值持有一个指向类型为typ的新申请的零值的指针
- t.Log("reflect.New", elem.Kind().String()) //ptr
- t.Log("reflect.New.Elem", elem.Elem().Kind().String()) //struct
- //model就是创建的User结构体变量(实例)
- model = elem.interface{}.("User") // model就是*User,它的指向和elem是一样的
- elem = elem.Elem() // 取得elem指向的值
- sv.FieldByName("UserId").SetString("123456")
- sv.FieldByName("Name").SetString("nickname")
- t.Log("model model.Name", model, model.Name)
- }
要求:
(1).编写一个Cal结构体,有两个字段 Num ,和 Num2
(2).方法 GetSub ( name string )
(3).使用反射遍历Cal结构体所有的字段信息
(4).使用反射机制完成对 GetSub 的调用,输出形式为: "mary 完成了减法运行, 9-5 = 4"