目录
问题引出,反射的使用场景
使用反射机制,编写函数的适配器,桥连接
基本介绍
- 反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type),类别(kind)
- 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
- 通过反射,可以修改变量的值,可以调用关联的方法。
- 使用反射,需要 import (“reflect”)
- 示意图
反射的应用场景
3)变量、interface{} 和 reflect.Value 是可以相互转换的,这点在实际开发中,会经常使用到。画出示意图
反射的快速入门
- 请编写一个案例,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作代码演示,见下面的表格:
- 请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作代码演示:
获取kind的时候,可以通过reflect.TypeOf(b).Kind()或者reflect.ValueOf(b).Kind()均可
package main
import (
"fmt"
"reflect"
)
// 专门演示反射
func reflectTest01(b interface{}) {
//通过反射获取到传入的变量的type,kind,值
//1、先获取到reflect.Type
rTyp := reflect.TypeOf(b)
fmt.Println("rTyp=", rTyp)
//2、获取到reflect.Value
rVal := reflect.ValueOf(b)
fmt.Println("rVal=", rVal)
num := rVal.Int() + 2
fmt.Println("num=", num)
//下面我们将rVal转成interface{}
iVal := rVal.Interface()
fmt.Println("iVal=", iVal)
//将interface转成需要的类型
v := iVal.(int)
fmt.Println("v=", v)
}
// 专门演示反射(对结构体的反射)
func reflectTest02(b interface{}) {
rTyp := reflect.TypeOf(b)
fmt.Println("rTyp=", rTyp)
rVal := reflect.ValueOf(b)
fmt.Printf("rVal=%v,rVal type= %T\n", rVal, rVal)
ival := rVal.Interface()
fmt.Printf("iVal=%v,ival type=%T\n", ival, ival)
v, ok := ival.(Student)
if ok {
fmt.Println("v=", v)
}
}
type Student struct {
Name string
Age int
}
func reflectTest03(b interface{}) {
rVal := reflect.ValueOf(b)
fmt.Println("rVal=", rVal)
fmt.Println(rVal.Float())
rTyp := reflect.TypeOf(b)
fmt.Println("rTyp=", rTyp)
//Rkind := rVal.Kind()
RKind := rTyp.Kind()
fmt.Println("RKind=", RKind)
Iv := rVal.Interface()
fmt.Println("Iv=", Iv)
v := Iv.(float64)
fmt.Println("v=", v)
}
func main() {
//请编写一个案例
//演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作
//1、先定义一个int
var num int = 100
reflectTest01(num)
stu := Student{
Name: "tom",
Age: 20,
}
reflectTest02(stu)
var fnum float64 = 1.2
reflectTest03(fnum)
}
反射的注意事项和细节
- reflect.Value.Kind,获取变量的类别,返回的是一个常量
2.Type 和 Kind 的区别
Type 是类型, Kind 是类别, Type 和 Kind 可能是相同的,也可能是不同的.
比如: var num int = 10 num 的 Type 是 int , Kind 也是 int
比如: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct
5)通过反射的来修改变量, 注意当使用 SetXxx 方法来设置需要通过对应的指针类型来完成, 这样
才能改变传入的变量的值, 同时需要使用到 reflect.Value.Elem()方
package main
import (
"fmt"
"reflect"
)
// 通过反射,修改
// num int 的值
// 修改student 结构体的值
func reflect01(b interface{}) {
//获取到reflect.Value
rVal := reflect.ValueOf(b)
kind1 := rVal.Kind()
fmt.Printf("kind1=%v\n", kind1)
rVal.Elem().SetInt(6)
}
func main() {
var num int = 10
reflect01(&num)
fmt.Println("num=", num)
}
6)reflect.Value.Elem() 应该如何理解?
反射实践
1)使用反射来遍历结构体的字段,调用结构体的方法,并获取结构体标签的值
package main
import (
"fmt"
"reflect"
)
// 定义了一个Monster 结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float32
Sex string
}
func (s Monster) Print() {
fmt.Println("-------start------")
fmt.Println(s)
fmt.Println("------end------")
}
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 TestStruct(a interface{}) {
//获取reflect.Type 类型
typ := reflect.TypeOf(a)
//获取reflect.Value 类型
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Struct {
fmt.Println("expect struct")
return
}
//获取该结构体有几个字段
num := val.NumField()
fmt.Printf("struct has %d fields\n", num)
for i := 0; i < num; i++ {
fmt.Printf("Field %d :值为=%v\n", i, val.Field(i))
//获取到struct标签,注意需要通过reflect.Type来获取tag标签的值
tagVal := typ.Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("Field %d:tag为=%v\n", i, tagVal)
}
}
numOfMethod := val.NumMethod()
fmt.Printf("struct has %d methods\n", numOfMethod)
//var params [] reflect.Value
//方法的排序默认是按照 函数名的排序(ASCII码)
val.Method(1).Call(nil)
//调用结构体的第一个方法Method(0)
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(50))
res := val.Method(0).Call(params) //传入的参数是[]reflect.Value
fmt.Println("res=", res[0].Int()) //返回结果,返回的结果是 [] reflect.Value
}
func main() {
//创建一个Monster实例
var a Monster = Monster{
Name: "黄鼠狼",
Age: 500,
Score: 66.9,
}
TestStruct(a)
}
2)使用反射的方式来获取结构体的 tag 标签, 遍历字段的值,修改字段值,调用结构体方法(要求: 通过传递地址的方式完成, 在前面案例上修改即可)
package main
import (
"fmt"
"reflect"
)
// 定义了一个Monster 结构体
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Score float32
Sex string
}
func (s Monster) Print() {
fmt.Println("-------start------")
fmt.Println(s)
fmt.Println("------end------")
}
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 TestStruct(a interface{}) {
//获取reflect.Type 类型
typ := reflect.TypeOf(a)
//获取reflect.Value 类型
val := reflect.ValueOf(a)
kd := val.Kind()
if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
}
//获取该结构体有几个字段
num := val.Elem().NumField()
val.Elem().Field(0).SetString("白象精")
fmt.Printf("struct has %d fields\n", num)
for i := 0; i < num; i++ {
fmt.Printf("Field %d :值为=%v\n", i, val.Elem().Field(i))
//获取到struct标签,注意需要通过reflect.Type来获取tag标签的值
tagVal := typ.Elem().Field(i).Tag.Get("json")
if tagVal != "" {
fmt.Printf("Field %d:tag为=%v\n", i, tagVal)
}
}
numOfMethod := val.Elem().NumMethod()
fmt.Printf("struct has %d methods\n", numOfMethod)
//var params [] reflect.Value
//方法的排序默认是按照 函数名的排序(ASCII码)
val.Elem().Method(1).Call(nil)
//调用结构体的第一个方法Method(0)
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(50))
res := val.Elem().Method(0).Call(params) //传入的参数是[]reflect.Value
fmt.Println("res=", res[0].Int()) //返回结果,返回的结果是 [] reflect.Value
}
func main() {
//创建一个Monster实例
var a Monster = Monster{
Name: "黄鼠狼",
Age: 500,
Score: 66.9,
}
TestStruct(&a)
fmt.Println("a.Name=", a.Name)
}
3)定义了两个函数 test1 和 test2,定义一个适配器函数用作统一处理接口【了解】
apply03_test.go
package apply03_test
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")
}
4)使用反射操作任意结构体类型:【了解】
apply04_test.go
package apply04_test
import (
"reflect"
"testing"
)
type user struct {
UserId string
Name string
}
func TestReflectStruct(t *testing.T) {
var (
model *user
sv reflect.Value
)
model = &user{}
sv = reflect.ValueOf(model)
t.Log("reflect.ValueOf", sv.Kind().String())
sv = sv.Elem()
t.Log("reflect.ValueOf.Elem", sv.Kind().String())
sv.FieldByName("UserId").SetString("123456")
sv.FieldByName("Name").SetString("nickname")
t.Log("model", model)
}
5)使用反射创建并操作结构体
apply05_test.go
package apply05_test
import (
"reflect"
"testing"
)
type user struct {
UserId string
Name string
}
func TestReflectStructPtr(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指向的类型
t.Log("reflect.TypeOf.Elem", st.Kind().String()) //struct
elem = reflect.New(st) //New返回一个Value类型值,该值持有一个指向类型为type的新申请的零值的指针
t.Log("reflect.New", elem.Kind().String()) //ptr
t.Log("reflect.New.Elem", elem.Elem().Kind().String()) // struct
//model就是创建的user结构体变量(实例)
model = elem.Interface().(*user) //model是*user 它的指向和elem是一样的
elem = elem.Elem() //取得elem指向的值
elem.FieldByName("UserId").SetString("123456") //赋值
elem.FieldByName("Name").SetString("nickname")
t.Log("model model.Name", model, model.Name)
}