反射引出
结构体的tag字段
利用反射机制编写函数的适配器(桥连接)
反射基本
1)反射可以在运动时动态获取变量的各种信息,比如变量的类型(type)类别(kind)
2)如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段,方法)
3)通过反射,可以修改变量的值,可以调用关联的方法
4)使用反射,要用包 reflect
反射示意图
反射重要的函数和概念
1)reflect.TypeOf(变量名),获取变量的类型返回reflect.Type类型
2)reflect.Value(变量名),获取变量的值,返回reflect.Value类型,reflect.Value是一个结构体
3)变量 空接口 reflect.Value是可以相互转换的,实际开发中会经常用到
eg:
相互转换
之间的关系
相互转换案例
package main
import (
"fmt"
"reflect"
)
//演示反射
func reflectTest01(a interface{}) {
//通过反射获取传入的变量, type,king,值
// 1.获取type
rtype := reflect.TypeOf(a)
fmt.Println(rtype)
// 获取到reflect.Value()
rVal := reflect.ValueOf(a)
fmt.Println(rVal)
rrVal := reflect.TypeOf(rVal)
fmt.Println(rrVal)
n1 := 20 + rVal.Int()
fmt.Println(n1)
//将rval转回interface
iv := rVal.Interface()
//将interface{}通过断言转回需要的类型
num := iv.(int)
fmt.Printf("%T", num)
}
type Student struct {
Name string
Age int
}
//对结构体的反射
func reflectTest02(a interface{}) {
// 1.获取type
rtype := reflect.TypeOf(a)
fmt.Println(rtype)
// 获取到reflect.Value()
rVal := reflect.ValueOf(a)
fmt.Println(rVal)
//获取对应的kind
//rVal.Kind()
//rtype.Kind()
fmt.Printf("kind=%v,kind=%v",rVal.Kind(),rtype.Kind())
//将rval转回interface
iv := rVal.Interface()
fmt.Printf("%T,,,%v\n", iv, iv)
//将interface{}通过断言转回需要的类型
//使用一个待检测的switch的断言形式
stu, ok := iv.(Student)
if ok {
fmt.Printf("%v", stu.Name)
}
}
func main() {
num := 100
reflectTest01(num)
fmt.Println("_____________________________________")
stu := Student{
Name: "tom",
Age: 100,
}
reflectTest02(stu)
}
反射细节和注意
1)reflect.Value.Kind()获取变量类别返回是一个常量
2)type是类型,Kind是类别。tpye和Kind可能相通也可能不相同
eg:
var num int=10 type=int Kind=int
var stu Student stu的Type是包名(pkg1).Student,King是struct
3)通过反射可以让变量在interface()和reflect.Value之间相互转换,这点在前面有示意图:
变量《------------》interface{}《----------》reflect.Value
否则,报错
5)反射修改变量值要使用reflect.Value.Elem()方法
package main
import (
"fmt"
"reflect"
)
func reflect01(a interface{}) {
rVal := reflect.ValueOf(a)
//传入地址的话,rVal是一个指针
fmt.Println(rVal.Kind())
// Elem返回v持有的接口保管的值的Value封装,或者v持有的指针指向的值的Value封装
rVal.Elem().SetInt(100)
}
func main() {
num := 1
reflect01(&num)
fmt.Println(num)
//可以这样理解rVal.Elem()
num1 := 9
ptr := &num1
num2 := *ptr
fmt.Println(num2)
}
反射最佳实践
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float64 `json:"score"`
Sex string `json:"sex"`
}
func (m Monster) Print() {
fmt.Println("-----start-----")
fmt.Println(m)
fmt.Println("----- end------")
}
func (m Monster) GetSum(n1 int, n2 int) int {
return n1 + n2
}
func (m *Monster) Set(name string, age int, score float64, sex string) {
m.Name = name
m.Age = age
m.Score = score
m.Sex = sex
}
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("传入参数不为结构体,已自动退出")
return
}
//获取该结构体题的字段数
num := val.NumField()
fmt.Println("该结构体有", num, "个字段")
//循环输出字段
for i := 0; i < num; i++ {
fmt.Printf("字段:%v,值为:%v,", i, val.Field(i))
tagval := typ.Field(i).Tag.Get("json")
if tagval != "" {
fmt.Printf("tag标签为%v,\n", tagval)
} else {
fmt.Println()
}
}
numMethod := val.NumMethod()
fmt.Println("该结构体有", numMethod, "个方法")
// 获取到第二个方法,并调用
//方法排序是按照函数名字母(ASCII码)进行的排序
val.Method(1).Call(nil)
// 调用结构体的第一个方法Method(0)
var params []reflect.Value
params = append(params, reflect.ValueOf(20))
params = append(params, reflect.ValueOf(72))
res := val.Method(0).Call(params)
fmt.Println(res[0].Int())
}
func main() {
mon := &Monster{
Name: "孙悟空",
Age: 1000,
Score: 100.2,
Sex: "男",
}
TestStruct(mon)
}
package main
import (
"fmt"
"reflect"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float64 `json:"score"`
Sex string `json:"sex"`
}
func (m *Monster) Print() {
fmt.Println("-----start-----")
fmt.Println(m)
fmt.Println("----- end------")
}
func (m *Monster) GetSum(n1 int, n2 int) int {
return n1 + n2
}
func (m *Monster) Set(name string, age int, score float64, sex string) {
m.Name = name
m.Age = age
m.Score = score
m.Sex = sex
}
func TestStruct(a interface{}) {
//获取reflect.Type类型
typ := reflect.TypeOf(a)
//获取reflect.Value类型
val := reflect.ValueOf(a)
// 获取到a对应的类别
kd := val.Kind()
if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
fmt.Println("传入参数不为结构体,已自动退出")
return
}
//获取该结构体题的字段数
num := val.Elem().NumField()
fmt.Println("该结构体有", num, "个字段")
//修改值
val.Elem().Field(0).SetString("刘广硕")
//循环输出字段
for i := 0; i < num; i++ {
fmt.Printf("字段:%v,值为:%v,", i, val.Elem().Field(i))
tagval := typ.Elem().Field(i).Tag.Get("json")
if tagval != "" {
fmt.Printf("tag标签为%v,\n", tagval)
} else {
fmt.Println()
}
}
numMethod := val.NumMethod()
fmt.Println("该结构体有", numMethod, "个方法")
// 获取到第二个方法,并调用
//方法排序是按照函数名字母(ASCII码)进行的排序
val.Method(1).Call(nil)
// 调用结构体的第一个方法Method(0)
var params []reflect.Value
params = append(params, reflect.ValueOf(20))
params = append(params, reflect.ValueOf(72))
res := val.Method(0).Call(params)
fmt.Println(res[0].Int())
}
func main() {
mon := &Monster{
Name: "孙悟空",
Age: 1000,
Score: 100.2,
Sex: "男",
}
TestStruct(mon)
}
package main
import (
"reflect"
"testing"
)
func TestReflectFunc(t *testing.T) {
call1 := func(v1 int, v2 int) {
t.Log(v1, v2)
}
call2 := func(v1 int, v2 int, s string) {
t.Log(v1, v2, s)
}
var (
function reflect.Value
invalue []reflect.Value
n int
)
bridge := func(call interface{}, args ...interface{}) {
n = len(args)
invalue = make([]reflect.Value, n)
for i := 0; i < n; i++ {
invalue[i] = reflect.ValueOf(args[i])
}
function = reflect.ValueOf(call)
function.Call(invalue)
}
bridge(call1, 1, 2)
bridge(call2, 1, 2, "test2")
}
package main
import (
"reflect"
"testing"
)
type user struct {
UserId string
Name string
}
func TestReflectStruct(t *testing.T) {
var (
model *user
st reflect.Type
elem reflect.Value
)
st = reflect.TypeOf(model) //获取类型*user
t.Log("reflect.TypeOf", st.Kind().String()) //ptr
st = st.Elem() //st指向的类型
elem = reflect.New(st)
t.Log("reflect.New", elem.Kind().String()) //ptr
t.Log("reflect.New.Elem", elem.Elem().Kind().String())
//model就是创建的user结构体变量
model = elem.Interface().(*user) //model是*user它的指向和elem是一样的
elem = elem.Elem() //取得elem指向的值
elem.FieldByName("UserId").SetString("12345678")
elem.FieldByName("Name").SetString("nickname")
t.Log("model model.Name", model, model.Name)
}
package main
import (
"fmt"
"math/rand"
"reflect"
"time"
)
type Cal struct {
Num1 int
Num2 int
}
func (c *Cal) GetSum(name string) string {
return fmt.Sprintf("%v完成了减法运算,%v - %v = %v", name, c.Num1, c.Num2, c.Num1-c.Num2)
}
func TestCal(c interface{}) {
// typ := reflect.TypeOf(c)
val := reflect.ValueOf(c)
num := val.Elem().NumField()
rand.Seed(time.Now().Unix())
for i := 0; i < num; i++ {
val.Elem().Field(i).SetInt(int64(rand.Intn(100)))
}
var params []reflect.Value
params = append(params, reflect.ValueOf("tom"))
res := val.Method(0).Call(params)
fmt.Println(res)
}
func main() {
c := &Cal{}
TestCal(c)
}