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 int
var x MyInt = 7
v := 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 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)
}
运行结果:
分析:
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)
}
留下个问题吧!
函数本事独立与任何个体之外存活的,方法却要依托对象的存在。方法是“对象”的一种行为。那么如何通过反射调用方法呢?
有疑问加站长微信联系(非本文作者)