1. 什么是反射

通俗来讲就是, go 语言中提供一种机制,可以在代码运行时获取变量的类型和值,这种机制就是反射。

反射是由 reflect 包提供支持. 它定义了两个重要的类型, Type 和 Value. 一个 Type 表示一个Go类型. 函数 reflect.TypeOf 接受任意的 interface{} 类型, 并返回对应动态类型的reflect.Type.

reflect 包中另一个重要的类型是 Value. 一个 reflect.Value 可以持有一个任意类型的值. 函数 reflect.ValueOf 接受任意的 interface{} 类型, 并返回对应动态类型的reflect.Value. 和 reflect.TypeOf 类似, reflect.ValueOf 返回的结果也是对于具体的类型, 但是 reflect.Value 也可以持有一个接口值.

反射的使用场景:写测试用例的时候可以使用反射

其他场景尽量不要使用反射,原因如下: 
        1. 业务代码中写反射,会增加复杂性,让人难以理解 
        2. 反射性能比较低,比正常代码要慢一到两个数量级 
        3. 反射并不能在编译时检查出错误,在运行时可能会出现panic 
        所以在正常代码中尽量不要使用反射。

2. 反射使用示例及分析

2.1 已知变量值,转化为对应的struct

  • 示例如下:
package mainimport ("fmt""reflect"
)type Addr struct {Province stringCity stringTelephone string
}func main() {addr := Addr {Province: "HuNan",City: "ShaoYang",Telephone: "15511111111",}reflectValue := reflect.ValueOf(addr)refectValueOfAddr, _ := reflectValue.Interface().(Addr)fmt.Printf("Province: %s, City: %s, Phone: %s\n", refectValueOfAddr.Province, refectValueOfAddr.City, refectValueOfAddr.Telephone)
}
  • TypeOf方法,ValueOf方法和FieldByName方法
type Addr struct {Province stringCity stringTelephone string
}type Student struct {Name stringAge  intSex  stringAddress Addr
}func testTypeOfAndValueOf() {fmt.Println("--------------start testTypeOfAndValueOf----------------")student := new(Student)student.Name = "python"fmt.Println(reflect.TypeOf(student))var student2 Studentstudent2.Name = "golang"fmt.Println(reflect.TypeOf(student2))fmt.Println(reflect.ValueOf(student2).FieldByName("Name"))fmt.Println("--------------end testTypeOfAndValueOf----------------")
}
  • CanSet方法
func testCanSet() {fmt.Println("--------------start testCanSet----------------")var student Studentstudent.Name = "golang"fmt.Println("reflect.ValueOf(student).FieldByName(\"Name\").CanSet()", reflect.ValueOf(student).FieldByName("Name").CanSet())fmt.Println("reflect.ValueOf(&(student.Name)).Elem().CanSet()", reflect.ValueOf(&(student.Name)).Elem().CanSet())c := "golang"p := reflect.ValueOf(&c)fmt.Println("p.CanSet() = ", p.CanSet())fmt.Println("p.Elem().CanSet() = ", p.Elem().CanSet())p.Elem().SetString("newName")fmt.Println("c = ", c)fmt.Println("--------------end testCanSet----------------")
}

2.2 type相关方法

package mainimport ("reflect""fmt"
)type lx interface {SayHi()
}type User struct {Name stringAge  int64Sex  string
}func (u *User) SayHi() {fmt.Println("hello world")
}func main() {user := User{"张三", 25, "男"}FillStruct(user)
}func FillStruct(obj interface{}) {t := reflect.TypeOf(obj)       //反射出一个interface{}的类型fmt.Println(t.Name())          //类型名fmt.Println(t.Kind().String()) //Type类型表示的具体分类fmt.Println(t.PkgPath())       //反射对象所在的短包名fmt.Println(t.String())        //包名.类型名fmt.Println(t.Size())          //要保存一个该类型要多少个字节fmt.Println(t.Align())         //返回当从内存中申请一个该类型值时,会对齐的字节数fmt.Println(t.FieldAlign())    //返回当该类型作为结构体的字段时,会对齐的字节数var u Userfmt.Println(t.AssignableTo(reflect.TypeOf(u)))  // 如果该类型的值可以直接赋值给u代表的类型,返回真fmt.Println(t.ConvertibleTo(reflect.TypeOf(u))) // 如该类型的值可以转换为u代表的类型,返回真fmt.Println(t.NumField())             // 返回struct类型的字段数(匿名字段算作一个字段),如非结构体类型将panicfmt.Println(t.Field(0).Name)          // 返回struct类型的第i个字段的类型,如非结构体或者i不在[0, NumField())内将会panicfmt.Println(t.FieldByName("Age"))     // 返回该类型名为name的字段(会查找匿名字段及其子字段),布尔值说明是否找到,如非结构体将panicfmt.Println(t.FieldByIndex([]int{0})) // 返回索引序列指定的嵌套字段的类型,等价于用索引中每个值链式调用本方法,如非结构体将会panic
}

2.3 value相关方法

package mainimport ("reflect""fmt"
)type User struct {Name  stringAge   intSex   boolPhone *stringQian  float64Atest uintGroup interface{}Btest interface{}
}func (u *User) Hello() {fmt.Println("hello world 你好世界")
}func main() {a := "hello world 你好世界"user := &User{"张三", 25, true, &a, 88.8, 9, 99, nil}var obj interface{} = userv := reflect.ValueOf(obj)method := v.MethodByName("Hello") //返回v的名为Hello的方法method.Call([]reflect.Value{})    //执行反射的方法fmt.Println(v.IsValid()) //返回v是否持有值,如果v是value零值会返回假,此时v除了IsValid String Kind之外的方法都会导致panicfmt.Println(v.Kind())    //返回v持有值的类型,如果是结构体类型,返回 structfmt.Println(v.Type())    //返回v持有值的类型Type表示,返回具体定义类型, 例如Userv = v.Elem() //返回持有的接口的值,或者指针的值,如果不是interface{}或指针会panic,实际上是从 *User到Uservar u Userfmt.Println(v.Convert(reflect.TypeOf(u)).FieldByName("Name")) //转换为其他类型的值,如果无法使用标准Go转换规则来转换,那么panicfmt.Println(v.FieldByName("Name").CanSet())   //是否可以设置Name的值v.FieldByName("Name").SetString("把Name值修改一下") //设置v的持有值,如果v的kind不是string或者v.Canset()返回假,会panicv.FieldByName("Name").Set(reflect.ValueOf(a)) //将v的持有值修改为a的反射值,如果Canset返回假,会panicfmt.Println(v.FieldByName("Group").Elem())     //返回持有的接口的值,或者指针的值,如果不是interface{}或指针会panicfmt.Println(v.FieldByName("Phone").Elem())     //或者指针的值fmt.Println(v.FieldByName("Name").Interface()) //把Name当做interface{}值fmt.Println(v.FieldByName("Name").String()) //返回v持有的值的字符串表示,如果v的值不是string也不会panicfmt.Println(v.FieldByName("Sex").Bool())    //返回持有的布尔值,如果v的kind不是bool会panicfmt.Println(v.FieldByName("Age").Int())     //返回持有的int64,如果v的kind不是int int8-int64会panicvar x int64fmt.Println(v.FieldByName("Age").OverflowInt(x)) //如果v持有值的类型不能无一出的表示x,会返回真,如果v的kind不是int int8-int64会panicfmt.Println(v.FieldByName("Atest").Uint())       //返回v持有的无符号整数,如果v的kind不是uint uintptr uint8 uint16 uint32 uint64会panicvar x2 uint64fmt.Println(v.FieldByName("Atest").OverflowUint(x2)) //如果v持有的值的类型不能无溢出的表示x2,会返回真,如果v的kind不是uint uintptr uint8 uint16 uint32 uint64会panicfmt.Println(v.FieldByName("Qian").Float())           //返回v持有的浮点数float64,如果v的kind不是float32 float64会panicvar x3 float64fmt.Println(v.FieldByName("Qian").OverflowFloat(x3)) //如果v持有值的类型不能无溢出的表示x3,会返回真,如果v的kind不是float32 float64会panicfmt.Println(v.FieldByName("Btest").IsNil())          //如果v持有值是否为nil,如果v的值不是通道 函数 接口 映射 指针 切片之一会panicfmt.Println(v.NumField())             //返回v持有的结构体类型值的字段数,如果v的kind不是struct会panicfmt.Println(v.Field(0))               //返回结构体的第i个字段,如果v的kind不是struct或i出界会panicfmt.Println(v.FieldByIndex([]int{0})) //和上面一样,没明白有啥用}

3. Type和value方法汇总

3.1 Type和Value拥有的同名方法

Type Value 备注
Kind Kind 表示 特定类型,go中定义的类型 ,如自己定义的 type Test struct {}, Kind 就是 struct
MethodByName MethodByName 根据方法名找方法
Method Method 返回第i个方法
NumMethod NumMethod 返回拥有的方法总数,包括unexported方法
Field Field 取struct结构的第n个field
FieldByIndex FieldByIndex 嵌套的方式取struct的field,比如v.FieldByIndex(1,2,3)等价于 v.field(1).field(2).field(3)
FieldByNameFunc FieldByNameFunc 返回名称匹配match函数的field
NumField NumField 返回struct所包含的field数量

3.2 Type独有的方法

方法名 备注
Align 分配内存时的内存对齐字节数
FieldAlign 作为struct的field时内存对齐字节数
Name type名 string类型
PkgPath 包路径, "encoding/base64", 内置类型返回empty string
Size 该类型变量占用字节数
String type的string表示方式
Implements 判断该类型是否实现了某个接口
AssignableTo 判断该类型能否赋值给某个类型
ConvertibleTo 判断该类型能否转换为另外一种类型
Comparable 判断该类型变量是否可以比较
ChanDir 返回channel的方向 recv/send/double
IsVariadic 判断函数是否接受可变参数
Elem 取该类型的元素
In 函数第n个入参
Out 函数第n个出参
NumIn 函数的入参数个数
NumOut 函数的出参个数
Key 返回map结构的key类型Type
Len 返回array的长度

3.3 Value独有的方法

方法名 备注
Addr v的指针,前提时CanAddr()返回true
Bool bool类型变量的值
Bytes []bytes类型的值
Call 调用函数
CallSlice 调用具有可变参的函数
CanAddr 判断能否取址
CanInterface 判断Interface方法能否使用
CanSet 判断v的值能否改变
Cap 判断容量 Array/Chan/Slice
Close 关闭Chan
Complex  
Convert 返回将v转换位type t的结果
Elem 返回interface包含的实际值
Float  
Index 索引操作 Array/Slice/String
Int  
Interface 将当前value以interface{}形式返回
IsNil 判断是否为nil,chan, func, interface, map, pointer, or slice value
IsValid 是否是可操作的Value,返回false表示为zero Value
Len 适用于Array, Chan, Map, Slice, or String
MapIndex 对map类型按key取值
MapKeys map类型的所有key的列表
OverflowComplex  
OverflowFloat 溢出判断
OverflowInt  
OverflowUint  
Pointer 返回uintptr 适用于slice
Recv chan接收
Send chan发送
Set 将x赋值给v,类型要匹配
SetBool  
SetBytes  
SetCap slice调整切片
SetMapIndex map赋值
SetUint  
SetPointer unsafe.Pointer赋值
SetString  
Slice return v[i:j] 适用于Array/Slict/String
String return value的string表示方法
TryRecv chan非阻塞接收
Try Send chan非阻塞发送
Type 返回value的Type
UnsafeAddr 返回指向value的data的指针

Golang reflect详解相关推荐

  1. PBFT -Golang实现详解

    PBFT -Golang实现详解 首相需要知道,PBFT算法的实现并不是听起来那么简单,我们通常听的这个算法的实现的的最为主要的,只是这种算法中的一个协议-----一致性协议. 另外还有检查点协议和视 ...

  2. Proxy和Reflect详解

    Proxy和Reflect详解 之前一直没有理解proxy代理是啥意思,只是感觉很深奥的样子,最近正好在研究vue3的响应式原理,发现vue3是使用proxy完成响应式的,因此仔细的研究了一下prox ...

  3. Golang Append()详解

    append函数的使用: append可以向一个slice中追加一个元素.多个元素.新的切片 var x []intx = append(x, 1) // 追加一个元素 x = append(x,2, ...

  4. Proxy与Reflect详解

    前言 现在国内的两大框架:vue.react.对于这两个框架,相信大家多多少少都接触过,对于vue而言,有一个很重要的特点,那就是响应式.vue2的响应式采用的是ES5的Object.definepr ...

  5. JavaScript 反射机制及 Reflect 详解

    来自 | https://www.cnblogs.com/Leophen/archive/2021/06/02/14838608.html 一.什么是反射机制 反射机制是在编译阶段不知道是哪个类被加载 ...

  6. JS 反射机制及 Reflect 详解

    一.什么是反射机制 反射机制是在编译阶段不知道是哪个类被加载,而是在运行的时候才加载.执行. 也就是说,反射机制指的是程序在运行时能够获取自身的信息. js 中的 apply 就是反射机制. 二.Re ...

  7. golang安装详解

    1.首先安装golang的类似java的windows安装包,即sdk1.9.2.可以从这里进行下载点击我进行go安装包下载 2.然后安装集成开发环境,goland如下图所示 点击我进行goland下 ...

  8. Golang iota详解

    iota是go语言的常量计数器,只能在常量表达式中使用 iota在const关键字出现时将被重置为0,const中每新增一行常量声明将使iota计数一次 可理解为const语句块中的行索引. 1.在常 ...

  9. golang slice 详解

    一.数组切片的使用: func main() {//1.基于数组创建数组切片var array [10]int = [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}var ...

  10. Golang GORM 详解

    学习目标: 1. GORM 介绍 2. GORM 使用 学习内容: 1 GORM介绍 GORM 是基于 Go 语言的 ORM 库,可使开发者对数据库使用更为方便,支持多种数据库. 它提供了许多功能,例 ...

最新文章

  1. pandas使用str函数和startswith函数,筛选dataframe中不是(not start with)以特定前缀开头的数据列(selecting columns)
  2. 分布式缓存技术memcached学习(一)——linux环境下编译memcahed
  3. YOLOX——Win10下训练自定义VOC数据集
  4. yaf 重写index.php,php框架Yaf路由重写实例代码
  5. 从新手到Flutter架构师,一篇就够!吐血整理
  6. 个人推动团队项目进展_推动者和关守者对开发团队的价值
  7. 博为峰Java技术文章 ——JavaSE Swing 如何使用进度条组件JProgressBarⅡ
  8. mysql binlog DDL_mysql一个事务中有DDL语句的binlog情况
  9. vb.net服务器启动后cpu占用了70_服务器如何区分攻击类型?
  10. 倒行逆施的贾金斯先生(转)
  11. jdbc连接oracle mysql_JDBC连接MySQL、Oracle和SQL server的配置
  12. java课程设计 扫雷_java扫雷游戏课程设计报告
  13. 解决vscode下载很慢的问题
  14. IT软件资产管理流程梳理介绍
  15. 兄弟连Linux笔记
  16. 手把手教你给小米游戏本安装macOS
  17. HTC One X S720e/G23刷ROOT教程
  18. Model、Map、ModelMap的关系
  19. ubuntu18.04 分辨率突然变小,添加1920x1080分辨率,转完变卡默认成集显llvmpipe改独显方法
  20. 历史最全、最细、近一年最新 知识图谱相关经典论文分享

热门文章

  1. / ./ ../相对路径详细解释
  2. Mac如何清理缓存文件
  3. npm 清理缓存命令 【最新的】
  4. Mac OS清除图标缓存
  5. CSS定位中绝对定位和固定定位(重点)
  6. 在vue中使用Animate.css
  7. 【车间调度】柔性作业车间调度问题的研究现状
  8. HttpClient介绍
  9. edge扩展下载失败或edge报错0x80072ee7
  10. 一道线性代数证明题:若AX=0的解空间为U,则U的正交补是由A的行向量组张成的