一、反射的基本介绍
反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
通过反射,可以修改变量的值,可以调用关联的方法。
使用反射,需要import (“reflect”)
二、反射的重要函数和概念
1、reflect.TypeOf(变量名) 获取变量的类型,返回reflect.Type类型
2、reflect.ValueOf(变量名) 获取变量的值,返回reflect.Value类型。reflect.Value是一个结构体类型。通过reflect.Value可以获取到关于该变量的很多信息。
3、变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中会经常使用到。
type Student struct {
Name string
Age int
}
func test(b interface{}) {
//interface{}转成reflect.Value
rVal := reflect.ValueOf(b)
//reflect.Value转换成interface{}
iVal := rVal.Interface()
//interface{}转换成原来的变量类型,使用类型断言
v := iVal.(Student)
fmt.Println(v)
}
对基本数据类型、interface{}、reflect.Value、结构体类型进行反射的基本操作
package main
import (
"fmt"
"reflect"
)
func reflectTest01(b interface{}) {
//通过反射获取传入变量的type、kind、值
//获取到reflect.Type
rType := reflect.TypeOf(b)
fmt.Println("rType=", rType)
//获取到reflect.Value
rVal := reflect.ValueOf(b)
n := 2 + rVal.Int()
fmt.Println("n=", n)
fmt.Printf("rVal=%v rVal type=%T
", rVal, rVal)
//将rVal转成interface{}
iV := rVal.Interface()
num := iV.(int)
fmt.Println("num=", num)
}
type Student struct {
Name string
Age int
}
func reflectTest02(b interface{}) {
rType := reflect.TypeOf(b)
fmt.Println("rType=", rType)
rVal := reflect.ValueOf(b)
iV := rVal.Interface()
fmt.Printf("iV=%v iV type=%T
", iV, iV)
stu, ok := iV.(Student)
if ok {
fmt.Printf("stu.Name=%v
", stu.Name)
}
}
func main() {
var num int = 100
reflectTest01(num)
stu := Student{
Name: "tom",
Age: 20,
}
reflectTest02(stu)
}
三、反射的注意细节
1、reflect.Value.Kind获取变量的类别,返回的是一个常量。
2、Type和Kind的区别:
Type是类型,Kind是类别,Type和Kind可能是相同的,也可能是不同的。
var num int=100 num的Type是int,Kind也是int
var stu Student stu的Type是pkg1.Student,Kind是struct
3、通过反射可以让变量在interface{}金额reflect.Value之间相互转换。
4、使用反射的方式来获取变量的值并返回对应的类型,要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).Int(),而不能使用其他的,否则报panic。
5、通过反射来修改变量,当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法。
package main
import (
"fmt"
"reflect"
)
func testInt(b interface{}) {
val := reflect.ValueOf(b)
fmt.Printf("val type=%T
", val)
val.Elem().SetInt(110)
fmt.Printf("val=%v
", val)
}
func main() {
var num int = 10
testInt(&num)
fmt.Println("num=", num)
}
变量var v float64=1.2,使用反射得到它的reflect.Value,然后获取到对应的Type,Kind和值,并将reflect.Value转换成interface{},再将interface{}转换成float64
package main
import (
"fmt"
"reflect"
)
func testFloat(b interface{}) {
val := reflect.ValueOf(b)
fmt.Printf("val =%v val type=%T
", val, val)
tVal := reflect.TypeOf(b)
fmt.Printf("tVal =%v
", tVal)
iT := val.Interface()
fNum := iT.(float64)
fmt.Printf("fNum=%v
", fNum)
}
func main() {
var num float64 = 1.2
testFloat(num)
}
修改字符串类型的值
package main
import (
"fmt"
"reflect"
)
func main() {
var str string = "tom"
fs := reflect.ValueOf(&str)
fs.Elem().SetString("jack")
fmt.Printf("str = %v
", str)
}
使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"monster_age"`
Score float32 `json:"成绩"`
Sex string
}
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
}
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()
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
//获取结构体有结果字段
num := val.NumField()
fmt.Printf("struct has %d fields
", num)
//结构体的所有字段
for i := 0; i < num; i++ {
fmt.Printf("Field %d:值为%v
", i, val.Field(i))
//获取struct标签,需要通过reflect.Type来获取tag标签的值
tagVal := typ.Field(i).Tag.Get("json")
//如果该字段有tag标签及显示,否则就不显示
if tagVal != "" {
fmt.Printf("Field %d:tag为%v
", i, tagVal)
}
}
//获取结构体有多少个方法
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods
", numOfMethod)
//方法的排序默认是按照函数名排序(ASCII码)
//调用结构体的第2个方法
val.Method(1).Call(nil)
//调用结构体的第1个方法
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
res := val.Method(0).Call(params)
fmt.Println("res=", res[0].Int())
}
func main() {
var a Monster = Monster{
Name: "黄鼠狼",
Age: 400,
Score: 30.8,
}
TestStruct(a)
}