go学习之反射知识
反射
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 main
import (
"fmt"
"reflect"
)
//专门反射演示
func reflectTest01(b interface{}) {
//通过反射获取的传入的变量 type,kind,值
//1.先获取到reflect.Type
rTyp :=reflect.TypeOf(b)
fmt.Println("rTyp=",rTyp) //int
//2.获取到reflectValue
rVal :=reflect.ValueOf(b) //100
// n1 := 10
// n2 := 2 + rVal
// fmt.Println("n2=",n2)//erro
// n1 := 10
n2 := 2 + rVal.Int()
fmt.Println("n2=",n2) //102
fmt.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、先定义一个int
var num int = 100
reflectTest01(num)
}
-2.请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
//专门演示对结构体的反射
func reflectTest02(b interface{}) {
//通过反射获取的传入的变量 type,kind,值
//1.先获取到reflect.Type
rTyp :=reflect.TypeOf(b)
fmt.Println("rTyp=",rTyp) //rTyp= main.Student
//2.获取到reflectValue
rVal :=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 string
Age 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 int num = 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 = 1 b = 2 ) }
2》还有一种专业的写法
func main(){ const( a = iota b c ) 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.Value
rVal := reflect.ValueOf(b)
rVal.Elem().SetInt(20)
}
func main() {
var num int = 10
reflect01(&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 main
import (
"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.9
reflect01(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 = name
s.Age = age
s.Score = score
s.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.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params) //传入的参数是[]reflect.Value,返回[]reflect.Value
fmt.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 string
Name string
}
func TestReflectStruct(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",,sv.kind().String())
sv.FieldByName("userId").SetString("12345678")
sv.FieldByName("Name").SetString("nickname")
t.Log("model",model)
}
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!