反射
文章目录
- 反射
- 1、反射的使用场景
- 1)结构体标签的应用
- 2)使用反射机制编写函数的适配器(桥连接)
- 2、反射的基本介绍
- -1.基本介绍
- -2.反射的图解
- -3.反射重要的函数和概念
- 3.反射快速入门
- -1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
- -2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
- 4.反射的注意事项和细节说明
- 1)reflect.Value.Kind,获取变量的类别,返回的是一个常量
- 2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
- 5.反射练习题
- 6.反射的最佳实践
1、反射的使用场景
1)结构体标签的应用
2)使用反射机制编写函数的适配器(桥连接)
2、反射的基本介绍
-1.基本介绍
1)反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
3)通过反射,可以修改变量的值,可以调用关联的方法
4)使用反射,需要import (“reflect”)
-2.反射的图解
-3.反射重要的函数和概念
1)reflect.TypeOf(变量名)。获取变量的类型,返回reflect.Type类型
2)reflect.ValueOf(变量名)。获取变量的值,返回reflect.Value类型reflect.Value是一个结构体类型。通过reflect.Value.可以获取到关于该变量的很多信息
3)变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常使用到。
3.反射快速入门
-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。
代码演示
package mainimport ("fmt""reflect")//专门反射演示func reflectTest01(b interface{}) {//通过反射获取的传入的变量 type,kind,值//1.先获取到reflect.TyperTyp :=reflect.TypeOf(b)fmt.Println("rTyp=",rTyp) //int//2.获取到reflectValuerVal :=reflect.ValueOf(b) //100// n1 := 10// n2 := 2 + rVal// fmt.Println("n2=",n2)//erro // n1 := 10n2 := 2 + rVal.Int()fmt.Println("n2=",n2) //102fmt.Printf("rVal=%v type=%T\n",rVal,rVal) //rVal=100 type=reflect.Value//下面我们将rVal转成interface{}iv := rVal.Interface()//将interface{}通过断言转成需要的类型num2 := iv.(int)fmt.Println("num2=",num2) //num2= 100}func main() {//-1.请编写一个函数,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。//1、先定义一个intvar num int = 100reflectTest01(num)}
-2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
//专门演示对结构体的反射func reflectTest02(b interface{}) {//通过反射获取的传入的变量 type,kind,值//1.先获取到reflect.TyperTyp :=reflect.TypeOf(b)fmt.Println("rTyp=",rTyp) //rTyp= main.Student//2.获取到reflectValuerVal :=reflect.ValueOf(b)//下面我们将rVal转成interface{}iv := rVal.Interface()fmt.Printf("iv=%v ,iv type=%T\n",iv,iv) //iv={Tom 20} ,iv type=main.Student//将interface{}通过断言转成需要的类型// fmt.Printf("iv=%v ,iv type=%T name\n",iv,iv,iv.Name) //erro无法取出name的值//所以先要进行断言stu,ok :=iv.(Student)if ok {fmt.Printf("stu.Name=%v\n",stu.Name)//stu.Name=Tom}}type Student struct {Name stringAge int}func main() {//2.定义一个Student的实例stu := Student{Name : "Tom",Age : 20,}reflectTest02(stu)}
4.反射的注意事项和细节说明
1)reflect.Value.Kind,获取变量的类别,返回的是一个常量
补充常量是知识
常量介绍
-
常量使用const修改
-
常量在定义的时候,必须初始化
-
常量不能修改
-
常量只能修饰bool、数值类型(int,float系列)、string类型
语法: const identifier [type] = value
package main import ("fmt" ) func main() {var num intnum = 90 //常量声明的时候必须赋值const tax int = 90// tax = 10 //常量是不能修改的fmt.Println(num,tax)//常量只能修饰bool、数值类型(int,float系列)、string类型//const b =num /3 erro }
常量使用注意事项
1》比较简洁的写法
func main () { const(a = 1b = 2 ) }
2》还有一种专业的写法
func main(){const(a = iotabc)fmt.Println(a,b,c)//0,1,2 }
3》Golang中没有常量名必须大写的规范
4》仍然通过首字母的大小来控制常量的访问范围
2)Type是类型,kind是类别,Type和Kind可能是相同的,也可能是不同的
比如: var num int = 10 num 的Type是int, Kind也是int
比如:var stu Student stu 的Type是包名.Student,Kind是struct(Kind的等级 >Type的等级)
3)通过反射可以让变量在interface{}和Reflect.Value之间相互转换
4)使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x)Int(),而不能使用其他的,否则报panic
5)通过反射的值来修改变量。注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的值,同时需要使用到reflect.Value.Elem()方法
package main
import ("fmt""reflect"
)//通过反射,修改
//num int 的值
//修改student的值func reflect01(b interface{}){//获取到reflect.ValuerVal := reflect.ValueOf(b)rVal.Elem().SetInt(20)
}
func main() {var num int = 10reflect01(&num)fmt.Println("num=",num) //20//你可以这样理解这句话:rVal.Elem()// num := 9// ptr *int = &num// num2 :=*ptr
}
6)如何理解rVal.Elem()
//你可以这样理解这句话:rVal.Elem()// num := 9// ptr *int = &num// num2 :=*ptr
}
5.反射练习题
1)给你一个变量 var v float64 = 1.2.请使用反射来得到它的reflect.Value,然后获取对应的Type,Kind和值,并将reflect.Value转换成interface{}.,再将interface{}转换成float64
package mainimport ("fmt""reflect")func reflect01(b interface{}) {num := reflect.ValueOf(b)kind1 :=num.Kind()iv :=num.Interface()fmt.Printf("b的reflect.Value是=%v,kind值为=%v,num转换为interface的值为=%v",num,kind1,iv)}func main() {var n float64 =65.9reflect01(n)}
2)给字符串改名题
var str string = "tom"fs :=reflect.ValueOf(&str) //这里要改成地址fs.Elem().SetString("jackma")fmt.Println(str)
6.反射的最佳实践
1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
2)使用反射的方式来获取结构体的tag标签,遍历字段的值,修改字段值,调用结构体方法
val.Method(0).Call() 调用第一个方法
方法的排序默认是按照函数名的排序(ASCII码)
定义了两个函数testl 和test2, 定义一个适配器函数用作统一 处理接口
使用反射操作任意结构体类型
使用反射创建并操作结构体
package main
import ("fmt""reflect"
)//定义了一个Monster结构体
type Monster struct{Name string `json:"name"`Age int `json:"age"`Score float32 `json:"成绩"`Sex string
}// 方法: 返回两数之和
func(s Monster) GetSum(n1, n2 int) int{return n1 + n2
}// 方法: 接收四个值, 给S赋值
func(s Monster) Set(name string, age int, score float32, sex string){s.Name = names.Age = ages.Score = scores.Sex = sex
}//方法,显示s的值
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()//如果传入的不是struct,就退出if kd != reflect.Struct {fmt.Println("Expect struct")return}//获取到该结构体有几个字段num := val.NumField()fmt.Printf("struct has %d fields\n", num) //4//变量结构体的所有字段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)//var params []reflect.Value//方法的排序默认是按照函数名的排序(ASCII码)val.Method(1).Call(nil)//获取到第二个方法。调用它//调用结构体的第1个方法Method(0)var params []reflect.Value //声明了[]reflect.Valueparams = append(params, reflect.ValueOf(10))params = append(params, reflect.ValueOf(40))res := val.Method(0).Call(params) //传入的参数是[]reflect.Value,返回[]reflect.Valuefmt.Println("res=", res[0].Int()) //返回结果,返回的结果是[]reflect.Value*/
}func main(){var a Monster = Monster{Name: "黄鼠狼精",Age: 400,Score: 30.8,} //将Monster实例传递给TestStruct函数TestStruct(a)
}
2)使用反射操作任意结构体类型
type user struct {UserId stringName string}func TestReflectStruct(t *testing.T){var {model *usersv reflect.Value}model = &user{}sv = reflect.ValueOf(model)t.Log("reflect.ValueOf",sv.kind().String())sv=sv.Elem()t.Log("reflect.ValueOf",,sv.kind().String())sv.FieldByName("userId").SetString("12345678")sv.FieldByName("Name").SetString("nickname")t.Log("model",model)}