go-反射
基本介绍
- 反射可以在运行时动态获取变量的各种信息,比如变量的类型,类别
- 若是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联方法。
- 使用反射,需要
import("reflect")
reflect.TypeOf(变量名)
获取变量的类型,返回reflect.Type类型
reflect.ValueOf(变量名)
获取变量的值,返回reflect.Value
类型reflect.Value
是一个结构体类型,通过reflect.Value
可以获取关于该变量的很多信息- 反射主要可用于序列化和反序列化
- 变量、interface{}和reflect.Value是可以相互转换的
快速入门
import (
"fmt"
"reflect"
)
type Person struct {
Name string
Age int
}
func main() {
testReflectBasic(20.5)
person := Person{"tom", 11} // rType: float64 kind: float64 rVal: 20.5
testReflectStruct(person) // rType: main.Person kind: struct
}
func testReflectBasic(temp interface{}) {
// 1. 获取reflect.Type
rType := reflect.TypeOf(temp)
fmt.Printf("rType: %v\n", rType)
// 2. 调用kind
kind := rType.Kind()
fmt.Printf("kind: %v\n", kind)
// 3. 获取reflect.Value
rVal := reflect.ValueOf(temp)
fmt.Printf("rVal: %v\n", rVal)
fmt.Printf("Type of rVal: %T\n", rVal) // Type of rVal: reflect.Value
n1 := rVal.Float()
fmt.Printf("n1: %v type: %T\n", n1, n1) // n1: 20.5 type: float64
// 将rVal转换为interfac{} 在使用断言
nums := rVal.Interface().(float64)
fmt.Printf("nums: %v type: %T\n", nums, nums) // nums: 20.5 type: float64
}
func testReflectStruct(temp interface{}) {
// 1. 获取reflect.Type
rType := reflect.TypeOf(temp)
fmt.Printf("rType: %v\n", rType)
// 2. 调用kind
kind := rType.Kind()
fmt.Printf("kind: %v\n", kind)
// 3. 获取reflect.Value
rVal := reflect.ValueOf(temp)
// 4. 查找字段
nameField := rVal.FieldByName("Name")
fmt.Printf("nameField: %v\n", nameField) // nameField: tom
// 5. 转换为interface{} 并断言
person := rVal.Interface().(Person)
fmt.Printf("person: %v\n", person) // person: {tom 11}
person.Name = "jack"
}
注意事项和细节
-
reflect.Value.Kind
,获取变量的类别,返回的是一个常量 -
Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的
var num int = 20 num 的 Type是int, Kind也是int
var stu Student stu 的 Type是包名.Student, Kind是struct
-
通过反射可以让变量在interface{}和reflect.Value之间相互转换
struct --> interface --> reflect.Vaue --> interface{} --> struct
-
使用反射的方式来获取变量的值,要求数据类型匹配,比如x是int,那么就应该使用
reflect.Value(x).Int()
,而不嫩他用其他的,否则panic -
通过反射的来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量值,同时需要使用reflect.Value.Elem()方法
-
reflect.Value.Elem()
type Student struct {
Name string
Age int
}
func main() {
stu := Student{"tom", 22}
test(&stu)
fmt.Printf("stu: %v\n", stu) // stu: {jack 22}
}
func test(temp interface{}) {
rVal := reflect.ValueOf(temp)
fmt.Printf("rVal: %v\n", rVal) // rVal: &{tom 22}
fmt.Printf("rVal type : %T\n", rVal) // rVal type : reflect.Value
fmt.Printf("rVal: %v\n", rVal.Kind()) // rVal: ptr 指针类型
fmt.Printf("rVal.Elem(): %v\n", rVal.Elem()) // rVal.Elem(): {tom 22} 结构体
rVal.Elem().FieldByName("Name").SetString("jack") // 改变Name
// 断言无法改变指针指向的结构体
stuVal := rVal.Elem()
fmt.Printf("stuVal: %v\n", stuVal) // stuVal: {jack 22}
fmt.Printf("stuVal: %T\n", stuVal) // stuVal: reflect.Value
// 转换 断言
stu := stuVal.Interface().(Student)
stu.Age = 33
}
案例
案例1
// 定义一个结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float64
Sex string
}
// 为结构体绑定方法Print
func (this Monster) Print() {
fmt.Println("-------- Print start -------")
fmt.Printf("this: %v\n", this)
fmt.Println("-------- Print end -------")
}
// 为结构体绑定方法GetSum
func (this Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
// 为结构体绑定方法Set
func (this Monster) Set(name string, age int, score float64, sex string) {
this.Name = name
this.Age = age
this.Score = score
this.Sex = sex
}
// 测试
func TestStuct(test interface{}) {
// 获取reflect.Type
rType := reflect.TypeOf(test)
// 获取reflect.Value
rVal := reflect.ValueOf(test)
// 字段类别
kind := rVal.Kind()
fmt.Printf("rType: %v\n", rType) // rType: main.Monster
fmt.Printf("rVal: %v\n", rVal) // rVal: {tom 22 99.3 nan}
fmt.Printf("kind: %v\n", kind) // kind: struct
// 判断类别是否是reflect.Struct
if kind != reflect.Struct {
fmt.Println("expect struct")
return
}
// 获取结构体字段个数
numField := rVal.NumField()
fmt.Printf("numField: %v\n", numField) // numField: 4
// 依次遍历每个字段
for i := 0; i < numField; i++ {
fmt.Printf("rVal.Field(%v): %v\n", i, rVal.Field(i)) // 获取字段的值,类型仍是reflect.Value
// 获取struct标签,需要通过reflect.Type来获取Tag标签
// Type 是一个interface, 其Field是一个结构体,其中包含了Tag
tagVal := rType.Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("tagVal: %v\n", tagVal)
}
}
// 根据Tag修改字段值
ageTag := rType.Field(1).Tag.Get("json")
fmt.Printf("ageTag: %T\n", ageTag) // ageTag: string
// 获取结构体方法个数
numMethod := rVal.NumMethod()
fmt.Printf("numMethod: %v\n", numMethod) // numMethod: 3
// 方法 并 调用
// 方法排序规则是依据方法名的ASCII码进行排序
rVal.Method(1).Call(nil)
// 调用结构体第一个方法并传参
// 反射调用时,Call() 中需要传递的是reflect.Value类型的切片
var params []reflect.Value
params = append(params, reflect.ValueOf(10), reflect.ValueOf(20))
// 反射调用后,返回的值仍是reflect.Value类型的切片
res := rVal.Method(0).Call(params)
fmt.Printf("res: %v\n", res) // res: [<int Value>]
fmt.Printf("res: %v\n", res[0]) // res: 30
}
func main() {
monster := Monster{"tom", 22, 99.3, "nan"}
TestStuct(monster)
}
案例2
func main() {
monster := Monster{"tom", 22, 99.3, "nan"}
fmt.Printf("monster: %v\n", monster) // monster: {tom 22 99.3 nan}
TestStuct(&monster)
fmt.Printf("monster: %v\n", monster) // monster: {jack 22 99.3 nan}
}
// 定义一个结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float64
Sex string
}
// 为结构体绑定方法Print
func (this Monster) Print() {
fmt.Println("-------- Print start -------")
fmt.Printf("this: %v\n", this)
fmt.Println("-------- Print end -------")
}
// 为结构体绑定方法GetSum
func (this Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
// 为结构体绑定方法Set
func (this Monster) Set(name string, age int, score float64, sex string) {
this.Name = name
this.Age = age
this.Score = score
this.Sex = sex
}
// 测试
func TestStuct(test interface{}) {
rVal := reflect.ValueOf(test)
fmt.Printf("rVal: %v\n", rVal)
// 字段
numField := rVal.Elem().NumField()
fmt.Printf("numField: %v\n", numField) //numField: 4
rVal.Elem().Field(0).SetString("jack")
// 方法
numMethod := rVal.NumMethod()
fmt.Printf("numMethod: %v\n", numMethod) // numMethod: 3
parameters := []reflect.Value{reflect.ValueOf(30), reflect.ValueOf(50)}
res := rVal.Elem().Method(0).Call(parameters)
fmt.Printf("res: %v\n", res) // res: [<int Value>]
fmt.Printf("res: %v\n", res[0]) // res: 80
}
案例3
func main() {
cal := Cal{30, 20}
testCal(cal)
}
type Cal struct {
Num1 int
Num2 int
}
func (this Cal) GetSub(name string) {
fmt.Println(name, " import getSub: ", this.Num1-this.Num2)
}
func testCal(test interface{}) {
rType := reflect.TypeOf(test)
rVal := reflect.ValueOf(test)
numField := rVal.NumField()
numMethod := rVal.NumMethod()
fmt.Printf("numField: %v\n", numField)
fmt.Printf("numMethod: %v\n", numMethod)
for i := 0; i < numField; i++ {
name := rType.Field(i).Name
val := rVal.Field(i)
fmt.Printf("name: %v\tval: %v\n", name, val)
// name: Num1 val: 30
// name: Num2 val: 20
}
for i := 0; i < numMethod; i++ {
methodVal := rType.Method(i).Name
fmt.Printf("methodVal: %v\n", methodVal) // methodVal: GetSub
}
var params []reflect.Value = []reflect.Value{reflect.ValueOf("tom")}
rVal.Method(0).Call(params)
}