前言
这是我参与「第五届青训营 」伴学笔记创作活动的第 14 天,其实第五届青训营已经正式开课n多天了,笔记不断更新中,都是我听完课之后的总结和平时自己的学习积累,分享出来给有需要的朋友。
本文内容
本文将涉及到Go语言反射的基本介绍和使用。
Go语言基础
1.反射reflect
(1)基本介绍
- 反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法。
- 使用反射,需要import("reflect")
(2)应用场景
反射常见应用场景有以下两种
1)不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。例如以下这种桥接模式
func bridge(funcPtr interface{},args ...interface{})
第一个参数funcPtr以接口的形式传入函数指针,函数参数args以可变参数的形式传入,bridge函数中可以用反射来动态执行funcPtr函数
2)对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。比如
type Message struct{
Name string
Age int
}
如果将上面的结构体实例序列化成json,但要求返回的json中字段名都要小写时就能用到反射,下面这样定义就能满足需求
type Message struct{
Name string `json:"name"`
Age int `json:"age"`
}
(3)重要的函数和概念
1)reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
2)reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型reflect.Value是一个结构体类型。通过reflect.Value,可以获取到关于该变量的很多信息。
type value
type Value struct {
// 内含隐藏或非导出字段
}
Value为go值提供了反射接口。
3)变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中,会经常用到。如图
(4)反射基本演示
1)演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作。如下
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}) {
//通过反射获取的传入的变量的type,kind值
//1.先获取到reflect.Type
rType := reflect.TypeOf(b)
fmt.Println("rType=", rType)
//2.获取到reflect.value
rVal := reflect.ValueOf(b) //注意这时rVal的值是10,但他不是int型,而且reflect.value型
fmt.Println("rVal=", rVal)
n2 := 10 + rVal.Int() //使用反射类型Int方法转成整型值
fmt.Println("n2=", n2)
fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)
// 下面我们将rVal 转成 interface{}
iV := rVal.Interface()
//将interface{} 通过类型断言 转成需要的类型
num2 := iV.(int)
fmt.Println("num2=", num2)
}
func main(){
//1.先定义一个int
var num int = 100
reflectTest01(num)
}
2)演示对(结构体、interface{}、reflect.Value)进行反射的基本操作。如下
package main
import (
"fmt"
"reflect"
)
//专门演示结构体的反射
func reflectTest02 (b interface{}) {
//通过反射获取的传入的变量 type ,kind值
//1. 先获取到reflect.Type
rType := reflect.TypeOf(b)
fmt.Println("rType=",rType)
//2.获取到reflect.Value
rVal := reflect.ValueOf(b)
fmt.Println("rVal=",rVal)
//下面我们将rVal转成inteface{}
iV := rVal.Interface()
fmt.Printf("iV=%v iV type=%T",iV,iV)
//将interface{}通过断言转成需要的类型
//可以使用switch的断言形式来做的更加灵活
stu,ok := iV.(Student)
if ok {
fmt.Printf("stu.Name=%v\n",stu.Name)
}
}
type Student struct {
Name string
Age int
}
func main() {
//2.定义一个Student的实例
stu := Student{
Name: "tom",
Age: 18,
}
reflectTest02(stu)
}
(5)反射注意事项和细节说明
1)reflect.Value.Kind,获取变量的类型,返回的是一个常量
2)Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的
比如:var num int=10中num的Type是int,Kind也是int 比如:var num int = 10 中 Type是包名.Student,Kind是struct
3)通过反射可以在让变量在interface{}和Reflect.Value之间相互转换
4)使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不能使用其它的,否则报panic
5)通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法
总结
- 1.反射可以从接口类型到反射类型对象
- 2.反射可以从反射类型对象到接口类型
- 3.修改反射类型变量的内部值需要保证其可设置性
写在最后
本文是我的日常学习笔记,如果哪里有写错,麻烦请指出来,感谢。这里我也推荐大家多写笔记,写笔记是一个很好的习惯,可以帮助我们更好的吸收和理解学习的新知识,新的一年大家一起加油!