What you are wasting today is tomorrow for those who died yesterday; what you hate now is the future you can not go back.
你所浪费的今天是昨天死去的人奢望的明天; 你所厌恶的现在是未来的你回不去的曾经。
Go是静态类型语言。每个变量都拥有一个静态类型,这意味着每个变量的类型在编译时都是确定的:int,float32, *AutoType, []byte, chan []int 诸如此类。
动静类型
编译时就知道变量类型的是静态类型;运行时才知道一个变量类型的叫做动态类型。
1. 静态类型
静态类型就是变量声明时的赋予的类型。比如:
type MyInt int // int 就是静态类型type A struct{ Name string // string就是静态}var i *int // *int就是静态类型
2. 动态类型
动态类型:运行时给这个变量复制时,这个值的类型(如果值为nil的时候没有动态类型)。一个变量的动态类型在运行时可能改变,这主要依赖于它的赋值(前提是这个变量时接口类型)。
var A interface{} // 静态类型interface{} A = 10 // 静态类型为interface{} 动态为int A = "String" // 静态类型为interface{} 动态为string var M *int A = M // 猜猜这个呢?
来看看这个例子:
//定义一个接口type Abc interface{ String() string}// 类型type Efg struct{ data string}// 类型Efg实现Abc接口func (e *Efg)String()string{ return e.data}// 获取一个*Efg实例func GetEfg() *Efg{ return nil}// 比较func CheckAE(a Abc) bool{ return a == nil}func main() { efg := GetEfg() b := CheckAE(efg) fmt.Println(b) os.Exit(1)}
关于动静态类型就到这里,详细请自行Google,百度吧。
反射
那么什么时候下使用反射呢?
有时候你想在运行时使用变量来处理变量,这些变量使用编写程序时不存在的信息。也许你正试图将来自文件或网络请求的数据映射到变量中。也许创建一个适用于不同类型的tool。在这些情况下,你需要使用反射。反射使您能够在运行时检查类型。它还允许您在运行时检查,修改和创建变量,函数和结构。
类型
你可以使用反射来获取变量的类型: var t := reflect.Typeof(v)。返回值是一个reflect.Type类型。该值有很多定义好的方法可以使用。
Name()
返回类型的名称。 但是像切片或指针是没有类型名称的,只能返回空字符串。
Kind()
Kind有slice, map , pointer指针,struct, interface, string , Array, Function, int或其他基本类型组成。Kind和Type之前要做好区分。如果你定义一个 type Foo struct {}, 那么Kind就是struct, Type就是Foo。
*小知识点:反射变量对应的Kind方法的返回值是基类型,并不是静态类型。下面的例子中:
type MyInt intvar x MyInt = 7v := reflect.ValueOf(x)
变量v的Kind依旧是reflect.Int,而不是MyInt这个静态类型。Type可以表示静态类型,而Kind不可以。
*注意点: 在使用refelct包时, reflect包会假定你已经知道所做的是什么,否则引发panic。 例如你调用了与当前reflect.Type 不同的类型上的方法,那么就会引发panic。
Elem()
如果你的变量是一个指针、map、slice、channel、Array。那么你可以使用reflect.Typeof(v).Elem()来确定包含的类型。
案例代码
type Foo struct { A int `tag1:"Tag1" tag2:"Second Tag"` B string}func main(){ // Struct f := Foo{A: 10, B: "Salutations"} // Struct类型的指针 fPtr := &f // Map m := map[string]int{"A": 1 , "B":2} // channel ch := make(chan int) // slice sl:= []int{1,32,34} //string str := "string var" // string 指针 strPtr := &str tMap := examiner(reflect.TypeOf(f), 0) tMapPtr := examiner(reflect.TypeOf(fPtr), 0) tMapM := examiner(reflect.TypeOf(m), 0) tMapCh := examiner(reflect.TypeOf(ch), 0) tMapSl := examiner(reflect.TypeOf(sl), 0) tMapStr := examiner(reflect.TypeOf(str), 0) tMapStrPtr := examiner(reflect.TypeOf(strPtr), 0) fmt.Println("tMap :", tMap) fmt.Println("tMapPtr: ",tMapPtr) fmt.Println("tMapM: ",tMapM) fmt.Println("tMapCh: ",tMapCh) fmt.Println("tMapSl: ",tMapSl) fmt.Println("tMapStr: ",tMapStr) fmt.Println("tMapStrPtr: ",tMapStrPtr)}// 类型以及元素的类型判断func examiner(t reflect.Type, depth int) map[int]map[string]string{ outType := make(map[int]map[string]string) // 如果是一下类型,重新验证 switch t.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Ptr, reflect.Slice: fmt.Println("这几种类型Name是空字符串:",t.Name(), ", Kind是:", t.Kind()) // 递归查询元素类型 tMap := examiner(t.Elem(), depth) for k, v := range tMap{ outType[k] = v } case reflect.Struct: for i := 0; i < t.NumField(); i++ { f := t.Field(i) // reflect字段 outType[i] = map[string]string{ "Name":f.Name, "Kind":f.Type.String(), } } default: // 直接验证类型 outType = map[int] map[string]string{depth:{"Name":t.Name(), "Kind":t.Kind().String()}} } return outType}
运行结果:
其中t.Field(index) 必须使用在Struct上 , 所以,细读文档才行
利用反射读取,设置,创建
看完了上面关于reflect检测变量类型外,我们使用反射读取、设置和创建。
要想读取一个变量的值,首先需要一个reflect.Valueof( var ) 实例(reflectVal := reflect.Valueof(var)), 同时也可以获取变量的类型了。
要想修改一个变量的值,那么必须通过该变量的指针地址 , 取消指针的引用 。通过refPtrVal := reflect.Valueof( &var )的方式获取指针类型,你使用refPtrVal.elem( ).set(一个新的reflect.Value)来进行更改,传递给set()的值也必须是一个reflect.value。
要想创建一个值,那么使用NewPtrVal := reflect.New( vartype ) 传递一个reflect.Type类型。 返回的指针类型就可以使用以上修改的方式写入值。
最后,你可以通过调用interface()方法返回一个正常的变量。因为Golang没有泛型,变量的原始类型丢失;该方法返回一个类型为interface{} 的值。如果创建了一个指针以便可以修改该值,则需要使用elem().interface()来反引用reflect的指针。在这两种情况下,您都需要将空接口转换为实际类型才能使用它。
实例代码:
type Foo struct { A int `tag1:"Tag1" tag2:"Second Tag"` B string}func main(){ // 反射的使用 s := "String字符串" fo := Foo{A: 10, B: "字段String字符串"} sVal := reflect.ValueOf(s) // 在没有获取指针的前提下,我们只能读取变量的值。 fmt.Println(sVal.Interface()) sPtr := reflect.ValueOf(&s) sPtr.Elem().Set(reflect.ValueOf("修改值1")) sPtr.Elem().SetString("修改值2") // 修改指针指向的值,原变量改变 fmt.Println(s) fmt.Println(sPtr) // 要注意这是一个指针变量,其值是一个指针地址 foType := reflect.TypeOf(fo) foVal := reflect.New(foType) // foVal.Elem().Field(0).SetString("A") // 引发panic foVal.Elem().Field(0).SetInt(1) foVal.Elem().Field(1).SetString("B") f2 := foVal.Elem().Interface().(Foo) fmt.Printf("%+v, %d, %s\n", f2, f2.A, f2.B)}
运行结果:
记忆10秒。
创建slice, map, chan
除了创建内置和用户定义类型的实例之外,还可以使用反射来创建通常需要make功能的实例。使用reflect.Makeslice,reflect.Makemap和reflect.Makechan函数来制作slice,map或channel。
// 反射创建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) fmt.Println(mapStringInt2)
运行结果:
创建函数
使用reflect.Makefunc()创建,这个函数需要我们想要做的函数的reflect.type和一个输入参数是[] reflect.value类型的slice,其输出参数也是类型[] reflect.value的闭包。下面是一个简单的例子,检测任意给定函数的执行时长:
package mainimport ( "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)}
运行结果:
分析:
reflect.Value.Call(var) 文档如下:
扩展Call()
首先我们可以确认一点就是,函数像普通变量一样, 假如Foo()是一个函数, 那么,f := Foo 也是成立的。
在反射中 函数 和 方法 的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value 的 Call() 方法,例如:
func Halou(){ fmt.Println("This is Halou函数! 6666")}func main(){ // Call()扩展 h := Halou hVal := reflect.ValueOf(h) fmt.Println("hVal is reflect.Func ?", hVal.Kind() == reflect.Func) hVal.Call(nil)}
运行结果:
reflect.Value 的 Call() 方法的参数是一个 reflect.Value 的 slice,对应所反射函数类型的参数,返回值也是一个 reflect.Value 的 slice,同样对应所反射函数类型的返回值。所以:
func Halou2(s string)string{ return "接受到参数:"+s+", 但这是返回值!"}func main(){ h2 := reflect.ValueOf(Halou2) params := []reflect.Value{ reflect.ValueOf("参数1"), } h2ReturnVal := h2.Call(params) fmt.Println(h2ReturnVal)}
留下个问题吧!
函数本事独立与任何个体之外存活的,方法却要依托对象的存在。方法是“对象”的一种行为。那么如何通过反射调用方法呢?