内容
1 获取接口类型值
2 修改接口类型值
3 反射调用函数
4 反射调用方法
5 reflect包api使用
先回顾一下反射三大定律:
1 反射可以将“接口类型变量”转换为“反射类型对象”;
2 反射可以将“反射类型对象”转换为“接口类型变量”;
3 如果要修改“反射类型对象”,其值必须是“可写的”(settable)。
一 反射获取接口类型值
通过接口变量获取变量指向的值和类型信息:
1 先通过接口变量获取value反射对象
2 通过value的Interface可以获取接口变量指向的对象,是interface类型
3 断言获取接口值
4 通过value对象type()方法获取接口变量的类型信息,是静态类型信息
5 通过type对象获取接口变量的种类信息,是具体的基本类型信息,如:int slice func map string等基础类型,在reflect/type有所有的基础类型kind
type MyType int
func main() {
var i = MyType(1)
iValue := reflect.ValueOf(i)
iType := reflect.TypeOf(i)
fmt.Printf(" i value: %v", iValue.Interface()) // i value: 1
fmt.Printf(" i type: %v", iValue.Type()) // i type: main.MyType
fmt.Printf(" i kind: %v", iType.Kind()) // i kind: int
}
// 获取结构体字段信息
// 结构体包含不可导出字段s,此时反射获取这个不可导出字段时,会panic
func reflectGetStructField() {
m := MyStruct{
A: 1,
B: true,
s: "test",
}
mValue := reflect.ValueOf(m)
mType := reflect.TypeOf(m)
for i:=0; i < mValue.NumField(); i++ {
v := mValue.Field(i)
fmt.Printf(" field: %v {type: %v; value:%v}", mType.Field(i).Name,
v.Type(), v.Interface())
}
}
//panic: reflect.Value.Interface: cannot return value obtained from unexported field or method
// 将s调整为S
输出:
field: A {type: int; value:1} field: B {type: bool; value: true} field: S {type: string; value: test}
二 修改接口类型值
要修改接口类型值,要谨记反射第三定律,其值要是可写的
总结为:
1 根据接口指针变量获取value对象;
2 根据Elem方法value变量具体指向的数据;
3 可以根据value对象的CanSet方法判断是否可写;
4 调用value对象的SetX方法进行改变;
func reflectModifyStructField() {
m := MyStruct{
A: 1,
B: true,
S: "test",
}
mValue := reflect.ValueOf(&m).Elem()
// 以下两种都是不行的,获取接口指针变量的value对象后,一定要调用Elem()方法解引用获取指向的具体数据,
// 因为我们对具体数据域修改,不是对mValue这个value 类型变量修改;
// 1 mValue := reflect.ValueOf(&m)
// 2 mValue := reflect.ValueOf(m)
mType := reflect.TypeOf(&m)
fmt.Println(fmt.Sprintf("%v can set: %v", mType, mValue.CanSet()))
for i:=0; i < mValue.NumField(); i++ {
if mValue.Field(i).Kind() == reflect.String {
mValue.Field(i).SetString("new test")
}
}
fmt.Println(fmt.Sprintf("%v : %v", mType, m))
}
// 输出:
*main.MyStruct can set: true
*main.MyStruct : {1 true new test}
三 反射调用函数
1 获取方法对象变量
2 获取value反射对象
3 构造方法入参slice, reflect.Value切片
4 调用value Call方法
5 得到执行结果,是value 切片
func Add(i, j int) int {
return i + j
}
func main() {
a := Add
aValue := reflect.ValueOf(a)
aParam := make([]reflect.Value, 2)
aParam[0] = reflect.ValueOf(1)
aParam[1] = reflect.ValueOf(2)
fmt.Println("reflect exe add, result: ", aValue.Call(aParam)[0])
}
// 输出
reflect exe add, result: 3
四 反射调用方法
1 获取value对象
2 构造方法入参slice, reflect.Value切片
3 根据MethodByName()获取方法value对象
4 调用call方法执行
type MyStruct struct {
A int
B bool
S string
}
func (m *MyStruct) Subtract(i, j int) int {
return i - j
}
func main() {
m := MyStruct{}
aParam := make([]reflect.Value, 2)
aParam[0] = reflect.ValueOf(1)
aParam[1] = reflect.ValueOf(2)
//方法是指针接收器,这里必须是指针变量
mValue := reflect.ValueOf(&m)
aParam[0] = reflect.ValueOf(1)
aParam[1] = reflect.ValueOf(2)
fmt.Println("reflect MyStruct Subtract, result: ", mValue.MethodByName("Subtract").Call(aParam)[0])
}
// 输出
reflect MyStruct Subtract, result: -1
五 reflect包api使用
1 创建slice map chan
// 反射创建map slice channel
intSlice := make([]int, 0)
mapStringInt := make(map[string]int)
sliceType := reflect.TypeOf(intSlice)
mapType := reflect.TypeOf(mapStringInt)
// 创建新值
intSliceReflect := reflect.MakeSlice(sliceType, 0, 0)
mapReflect := reflect.MakeMap(mapType)
// 使用新创建的变量
v := 10
rv := reflect.ValueOf(v)
intSliceReflect = reflect.Append(intSliceReflect, rv)
intSlice2 := intSliceReflect.Interface().([]int)
fmt.Println(intSlice2)
k := "hello"
rk := reflect.ValueOf(k)
mapReflect.SetMapIndex(rk, rv)
mapStringInt2 := mapReflect.Interface().(map[string]int)
2 创建函数
使用reflect.Makefunc()创建函数,入参是:想要创建的函数的reflect.type和一个输入参数是[] reflect.value类型的slice,其输出参数也是类型reflect.value 切片的闭包
package main
import (
"reflect"
"time"
"fmt"
"runtime"
)
/*
将创建Func封装, 非reflect.Func类型会panic
当然makeFunc的闭包函数表达式类型是固定的,可以查阅一下文档。
细读文档的reflect.Value.Call()方法。
*/
func MakeTimedFunction(f interface{}) interface{} {
rf := reflect.TypeOf(f)
if rf.Kind() != reflect.Func {
panic("非Reflect.Func")
}
vf := reflect.ValueOf(f)
wrapperF := reflect.MakeFunc(rf, func(in []reflect.Value) []reflect.Value {
start := time.Now()
out := vf.Call(in)
end := time.Now()
fmt.Printf("calling %s took %v\n", runtime.FuncForPC(vf.Pointer()).Name(), end.Sub(start))
return out
})
return wrapperF.Interface()
}
func time1() {
fmt.Println("time1Func===starting")
time.Sleep(1 * time.Second)
fmt.Println("time1Func===ending")
}
func time2(a int) int {
fmt.Println("time2Func===starting")
time.Sleep(time.Duration(a) * time.Second)
result := a * 2
fmt.Println("time2Func===ending")
return result
}
func main() {
timed := MakeTimedFunction(time1).(func())
timed()
timedToo := MakeTimedFunction(time2).(func(int) int)
time2Val := timedToo(5)
fmt.Println(time2Val)
}
引用文档:Golang Reflect反射的使用详解1 https://my.oschina.net/90design/blog/1614820