1.介绍
Go
语言实现了反射,所谓反射就是动静运行时的状态。咱们个别用到的包是reflect
包,reflect
实现了运行时的反射能力,可能让程序操作不同类型的对象。反射包中有两对十分重要的函数和类型,两个函数别离是:
办法名 | 形容 |
---|---|
reflect.TypeOf | 能够取得任意值的类型对象,返回类型:reflect.Type |
reflect.ValueOf | 能够取得任意值的值对象,返回类型:reflect.Value |
反射包中的所有办法根本都是围绕着reflect.Type
和reflect.Value
两个类型设计的。咱们通过reflect.TypeOf
、reflect.ValueOf
能够将一个一般的变量转换成反射包中提供的reflect.Type
和reflect.Value
,随后就能够应用反射包中的办法对它们进行简单的操作。
2.reflect.Type
类型 reflect.Type
是反射包定义的一个接口,咱们能够应用 reflect.TypeOf
函数获取任意变量的类型,reflect.Type
接口中定义了一些办法,罕用的如下:
2.1 罕用办法列表
办法名 | 形容 |
---|---|
Kind() Kind | 返回该变量的的具体分类 |
Name() string | Name返回该类型在本身包内的类型名,如果是未命名类型会返回"" |
NumField() int | 返回 struct 字段数量,不是 struct 会 panic |
Implements(u Type) bool | 查看以后类型有没有实现接口 u |
Field(i int) StructField | 返回struct 类型的第 i 个字段,不是struct 会 panic ,<br/>i 越界也会panic |
Key() Type | 返回map 的key 的类型,不是map 会panic |
2.2 应用示例
package mainimport ( "fmt" "reflect")type People interface { Eat(str string)}type User struct { Name string Age int}func (u User) Eat(str string) { fmt.Println("吃" + str)}func main() { var user User userTypeOf := reflect.TypeOf(user) fmt.Printf("typeOf:%T v:%v\n", userTypeOf, userTypeOf) // 获取类型分类 fmt.Printf("kind:%T v:%v\n", userTypeOf.Kind(), userTypeOf.Kind()) // 获取类型名称 fmt.Printf("name:%T v:%v\n", userTypeOf.Name(), userTypeOf.Name()) // 判断类型是否实现接口People implements := userTypeOf.Implements(reflect.TypeOf((*People)(nil)).Elem()) fmt.Printf("变量user是否实现接口People : %t\n", implements) // 变量构造体user的字段数量 fmt.Printf("变量构造体user的字段数量: %d \n", userTypeOf.NumField()) // 打印构造体字段 fmt.Printf("打印构造体user第%d个字段: %v \n", 0,userTypeOf.Field(0).Name) fmt.Printf("打印构造体user第%d个字段: %v \n", 1,userTypeOf.Field(1).Name) map1 := map[string]int { "张三": 19, "李四": 23, } fmt.Printf("打印map的key类型: %v \n", reflect.TypeOf(map1).Key())}/**输入typeOf:*reflect.rtype v:main.Userkind:reflect.Kind v:structname:string v:User变量user是否实现接口People : true变量构造体user的字段数量: 2 打印构造体user第0个字段: Name 打印构造体user第1个字段: Age 打印map的key类型: string */
2.3 Kind类型整顿
type Kind uintconst ( Invalid Kind = iota // 非法类型 Bool // 布尔型 Int // 有符号整型 Int8 // 有符号8位整型 Int16 // 有符号16位整型 Int32 // 有符号32位整型 Int64 // 有符号64位整型 Uint // 无符号整型 Uint8 // 无符号8位整型 Uint16 // 无符号16位整型 Uint32 // 无符号32位整型 Uint64 // 无符号64位整型 Uintptr // 指针 Float32 // 单精度浮点数 Float64 // 双精度浮点数 Complex64 // 64位复数类型 Complex128 // 128位复数类型 Array // 数组 Chan // 通道 Func // 函数 Interface // 接口 Map // 映射 Ptr // 指针 Slice // 切片 String // 字符串 Struct // 构造体 UnsafePointer // 底层指针)
3.reflect.Value
反射包中Value
的类型与Type
不同,它被申明成了构造体。这个构造体没有对外裸露的字段,然而提供了获取或者写入数据的办法。
3.1 罕用办法列表
办法名 | 形容 |
---|---|
Call(in []Value) []Value | 调用办法,第一个参数是in[0] ,第二个是in[1] ,以此类推 |
Field(i int) StructField | 返回struct 类型的第 i 个字段的值,不是struct 会 panic ,<br/>i 越界也会panic 。 |
FieldByName(name string) Value | 依据字段名查找值,前提类型是struct |
Index(i int) Value | 返回第 i 个元素,次要用于遍历,不能越界。<br/>前提类型是Array, Slice, String 之一, |
IsNil() bool | 返回值是否为nil 。如果值类型不是通道(channel )、函数、接口、 map 、指针或 切片时产生panic ,相似于语言层的v== nil 操作。常被用于判断指针是否为空。 |
IsValid() bool | 返回v是否持有一个值。如果v 是Value 零值会返回假,此时v除了IsValid、String、Kind之外的办法都会导致panic 。常被用于断定返回值是否无效 |
MapIndex(key Value) Value | 依据索引获取对应的值,前提类型是map |
MapKeys() []Value | 返回map 中所有的key ,返回类型是切片slice |
Set(x Value) | 通过反射批改值。 |
Type() Type | 获取值的类型 |
3.2 应用示例
package mainimport ( "fmt" "reflect")type User struct { Name string Age int}func (u User) Eat(str string) { fmt.Println("调用函数: 吃" + str)}func main() { user := User{"张福生", 30} userValOf := reflect.ValueOf(user) // 调用办法 param := []reflect.Value{reflect.ValueOf("香蕉")} userValOf.MethodByName("Eat").Call(param) // 打印构造体的值 fmt.Printf("打印构造体user第%d个字段值v: %v\n", 0, userValOf.Field(0)) fmt.Printf("打印构造体user第%d个字段值v: %v\n",1, userValOf.Field(1)) // 依据属性值打印构造体的值 fmt.Printf("打印构造体user字段%s的值v: %v\n", "Age",userValOf.FieldByName("Age")) // 返回第 i 个元素,次要用于遍历,不能越界。前提类型是Array, Slice, String之一, strSlice :=[]string{"你","我","他","!"} strSliceValOf := reflect.ValueOf(strSlice) fmt.Printf("打印字符串第%d个元素值v: %v\n", 0,strSliceValOf.Index(0)) fmt.Printf("打印字符串第%d个元素值v: %v\n", 1,strSliceValOf.Index(1)) fmt.Printf("打印字符串第%d个元素值v: %v\n", 3,strSliceValOf.Index(2)) // IsNil: 判断是否是nil fmt.Printf("变量strSlice的值是否是nil: %t\n",strSliceValOf.IsNil()) var u []User uuOf := reflect.ValueOf(u) fmt.Printf("IsNil: 变量u的值是否是nil: %t\n",uuOf.IsNil()) // IsValid:判断返回值是否无效 fmt.Printf("存在的办法: %t\n",userValOf.MethodByName("Eat").IsValid()) fmt.Printf("不存在的办法: %t\n",userValOf.MethodByName("A").IsValid()) // ############# 针对map的操作 ############# map1 := map[string]string{ "姓名": "张三", "phone": "176666666666", "?": "ABC", } mapValOf := reflect.ValueOf(map1) // 依据索引获取对应的值 fmt.Printf("依据索引获取map值,key=%s,v=%s \n","姓名",mapValOf.MapIndex(reflect.ValueOf("姓名"))) fmt.Printf("依据索引获取map值,key=%s,v=%s \n","phone",mapValOf.MapIndex(reflect.ValueOf("phone"))) fmt.Printf("依据索引获取map值,key=%s,v=%s \n","?",mapValOf.MapIndex(reflect.ValueOf("?"))) // 返回map中所有的key,返回类型是切片slice fmt.Printf("返回map中所有的key: %+v 类型:%T \n",mapValOf.MapKeys(),mapValOf.MapKeys()) // 通过反射批改值 num := 10 numValOf := reflect.ValueOf(&num)//留神这里须要传地址 fmt.Printf("批改前的值: %v \n",num) numValOf.Elem().Set(reflect.ValueOf(100)) fmt.Printf("批改后的值: %v \n",num) // 获取值的类型 fmt.Printf("获取mapValOf值的类型: %v \n",mapValOf.Type())}
输入:
调用函数: 吃香蕉打印构造体user第0个字段值v: 张福生打印构造体user第1个字段值v: 30打印构造体user字段Age的值v: 30打印字符串第0个元素值v: 你打印字符串第1个元素值v: 我打印字符串第3个元素值v: 他变量strSlice的值是否是nil: falseIsNil: 变量u的值是否是nil: true存在的办法: true不存在的办法: false依据索引获取map值,key=姓名,v=张三 依据索引获取map值,key=phone,v=176666666666 依据索引获取map值,key=?,v=ABC 返回map中所有的key: [姓名 phone ?] 类型:[]reflect.Value 批改前的值: 10 批改后的值: 100 获取mapValOf值的类型: map[string]string
4.实际场景
4.1 动静调用构造体办法
应用MethodByName("xx").Call(param)
调用函数
/** * @Author Mr.LiuQH * @Description 反射学习应用 * @Date 2021/2/2 10:50 上午 **/package mainimport ( "fmt" "reflect")type People struct { Name string}func (p People) GetName() string { return p.Name}func (p People) Tell(toName, context string) string { return fmt.Sprintf(p.Name+"通知%s,%s", toName, context)}func main() { p := People{"张三"} peopleValOf := reflect.ValueOf(p) // 通过反射调用办法(无参数) call := peopleValOf.MethodByName("GetName").Call(nil) fmt.Printf("无参函数调用,返回: %v %T \n", call, call) // 通过反射调用办法(有参数) param := []reflect.Value{ reflect.ValueOf("小明"), reflect.ValueOf("你通过考试了!"), } values := peopleValOf.MethodByName("Tell").Call(param) fmt.Printf("有参函数调用,返回: %v %T \n", values, values)}/**输入无参函数调用,返回: [张三] []reflect.Value 有参函数调用,返回: [张三通知小明,你通过考试了!] []reflect.Value */
4.2 处理错误后果
package mainimport ( "errors" "fmt" "reflect")type People struct { Name string}func (p People)ThrowError()error { return errors.New("测试谬误抛出")}func main() { p := People{"张三"} peopleValOf := reflect.ValueOf(p) ret := peopleValOf.MethodByName("ThrowError").Call(nil) // 返回值也是 Value 类型,对于谬误,能够转为 interface 之后断言 fmt.Printf("errValue: %v 类型转换: %T", ret[0], ret[0].Interface().(error))}
4.3 解析构造体标签
应用field.Tag.Lookup()
或field.Tag.Get()
查找对应的标签值
package mainimport ( "fmt" "reflect")type People struct { Name string `json:"name" db:"t_name"` Age int `json:"age" db:"t_age"`}func main() { p := People{"张三",23} peopleTypeOf := reflect.TypeOf(p) // 获取构造体字段数量 fieldNum := peopleTypeOf.NumField() for i := 0; i < fieldNum; i++ { // 获取构造体中,第i个字段的值 field := peopleTypeOf.Field(i) // 获取json标签对应的值(办法一) if lookup, ok := field.Tag.Lookup("json");ok { fmt.Printf("字段 %v, 对应的json-tag: %s \n" ,field.Name, lookup) } // 获取db标签对应的值(办法二) dbTag := field.Tag.Get("db") fmt.Printf("字段 %v, 对应的db-tag: %s \n" ,field.Name, dbTag) }}/**输入字段 Name, 对应的json-tag: name 字段 Name, 对应的db-tag: t_name 字段 Age, 对应的json-tag: age 字段 Age, 对应的db-tag: t_age */
4.4 判断是否实现接口
package mainimport ( "fmt" "reflect")type People interface { Eat(str string)}type User struct {}func (u User) Eat(str string) { fmt.Println("吃" + str)}func main() { user := &User{} // 当变量不是传指针时,Eat办法的接管方是指针,则没有实现接口 userTypeOf := reflect.TypeOf(user) // 判断类型是否实现接口People implements := userTypeOf.Implements(reflect.TypeOf((*People)(nil)).Elem()) fmt.Printf("变量user是否实现接口People : %t\n", implements)}// 输入: 变量user是否实现接口People : false
本文由mdnice多平台公布