Go中也提供了反射机制,与Java同样Go的反射也是在运行时获取对象的相关信息,更新对象内部状态;Golang经过反射能够获取对象类型、字段类型与值、调用struct实例方法、更新实例值等;
Go关于反射相关的对象、函数都在reflect包中最主要的两个为:Type与Value;
Go提供了下面两个函数,这两个是Go反射的核心;
reflect.TypeOf 返回目标对象的类型
reflect.ValueOf 返回值目标对象的值json
t:=1 fmt.Println(reflect.TypeOf(t), reflect.ValueOf(t)) 输出:int 1
type Demo struct { Id int Name string } func (d *Demo) Back() { fmt.Println("调用方法 Back") } func (d *Demo) Add(a, b int) int { return a + b }
d := &Demo{Id: 2, Name: "test"} getValue(d) 输出: Id : 2 Name : test func getValue(v interface{}) { t := reflect.TypeOf(v) o := reflect.ValueOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() //获取类型指针中的类型 } if o.Kind() == reflect.Ptr { o = o.Elem() //获取值地址中的值 } num := t.NumField() //获取字段个数 for i := 0; i < num; i++ { f := o.Field(i) //获取字段的值 fieldName := t.Field(i).Name //获取字段名称 switch f.Kind() { case reflect.Int: fmt.Println(fieldName, ": ", f.Int()) case reflect.String: fmt.Println(fieldName, ": ", f.String()) default: fmt.Println("类型不不支持") } } }
对于引用类型使用reflect.TypeOf返回的是该类型的指针,reflect.ValueOf返回的是该类型的值地址;因此对于引用类型都要的相关操做都要调用Elem()函数获取真实的类型与值;
调用Type或Value对象的NumField函数获取结构体的字段个数
调用Value对象的Field(i) 可获取字段的值;
调用Value对象的Kind()函数可获取字段的类型;
该Value对应于哪一种类型调用对应的函数便可获取获得相应的值,如类型不一致将抛出:panic: reflect: call of reflect.Value.Xxx on int Value;函数
d := new(Demo) setValue(d) fmt.Println(d) 输出:&{88 Test} func setValue(v interface{}) { t := reflect.TypeOf(v) o := reflect.ValueOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() } if o.Kind() == reflect.Ptr { o = o.Elem() } num := t.NumField() for i := 0; i < num; i++ { f := o.Field(i) switch f.Kind() { case reflect.Int: f.SetInt(88) //往该字段设值 case reflect.String: f.SetString("Test") /往该字段设值 default: fmt.Println("类型不支持") } } }
修改字段值与获取值同样,类型必定要一致,如不一致将抛异常,如int类型却调用SetString设值:panic: reflect: call of reflect.Value.SetString on int Value;指针
d := new(Demo) callMethod(d) 输出:调用方法 Back func callMethod(v interface{}) { o := reflect.ValueOf(v) o.MethodByName("Back").Call(nil) }
调用MethodByName根据名称获取方法,Call调用该方法;code
d := new(Demo) callMethodParam (d) 输出:3 func callMethodParam(p interface{}) { o := reflect.ValueOf(p) args:=[]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)} v:= o.MethodByName("Add").Call(args) fmt.Println(v[0]) }
比较经常使用的方法还有:
使用名字获取结构体的成员
Refletct.ValueOf(*e).FieldByName(“Name”)
获取结构体成员的json标记信息
reflect.TypeOf(s) . Field(0).Tag.Get(“key”)对象
Golang的反射也遵循Go语言规则,反射没法修改结构体中的私有对象,没法调用私有私有方法,可访问私有成员,修改私有成员将会抛出reflect.flag.mustBeAssignable异常;blog
文章首发地址:Solinx
https://mp.weixin.qq.com/s/W0UVbFxMMeXA5HuNNZlK-Aget