您的位置:首页 > 编程语言 > Go语言

golang中反射知识点浅析

2017-03-07 22:27 190 查看

1.reflect简介

反射,就是在程序运行过程中,能够获取任意一个类的属性,方法;对任意一个类,能够调用他的任意方法和属性。这是一个比较官方的解说。个人觉得通俗易懂的解释是:“通过一个给定的变量对象,获取这个变量的类型,这个类型的属性方法

2.重要接口与类

golang中reflect包

package reflect


重要接口

type Type interface{}


重要类

type Value struct{}


关键点

反射类也是有类型的,golang中的其他类型,怎么和反射搭上关系呢?

与Type接口类型变量勾搭上的方法是:

func TypeOf(i interface{}) Type


与Value类类型勾搭上的方法是

func ValueOf(i interface{}) Value


上边两个函数,均是reflect包中的导出方法。可以在程序中使用,在与反射勾搭上之前,你只需要有一个变量对象,将这个变量,传入上边任何一个中,就可以与golang的反射勾搭上。引用不同的函数,返回值不一样,顾名思义,一个偏向于类型,一个偏向于值,通过ValueOf函数与反射勾搭上,具有更丰富的功能。

example:

func main() {
str := []string{"go", "lang", "reflect", "example"}
val := reflect.ValueOf(str)
fmt.Println(val)
}


如上图所示,str是一个变量对象,通过reflect.ValueOf函数,将str变量对象与反射成功的勾搭上。

3. 反射能做什么

3.1 获取变量对象类型

reflect包中Value类提供了Kind方法,可以获取变量对象的类型

func main() {
str := []string{"go", "lang", "reflect", "example"}
val := reflect.ValueOf(str)
vtype := val.Kind()
fmt.Println(vtype)
}
//Output: slice


上边的例子是不是很傻?自己定义的str怎么可能不知道自己的类型呢?还假装自己不知道,用听起来高大上的反射做这么愚蠢的事情。下边再来一个例子:

func WhatIsYourType(i interface{}) string {
val := reflect.ValueOf(i)
return val.Kind().String()
}

func main() {
str := []string{"go", "lang", "reflect", "example"}
it := 2009
sit := map[string]string{"hi": "girl", "hello": "sir"}
fmt.Println(WhatIsYourType(str))
fmt.Println(WhatIsYourType(it))
fmt.Println(WhatIsYourType(sit))
}
//Output:
//   slice
//   int
//   map


假如有这么一个需求,一个函数,需要接收所有类型的变量,根据接收变量的类型,完成不同的操作。golang是一门强类型语言,自然这个函数如果想接收所有的类型,最好的办法就是申明这个参数类型为interface{},当这个函数接收到interface{}类型时,如果想知道传进来的那个变量真实类型,最好的办法是,通过反射来解决这个需求了。

上边通过Value提供的Kind方法,获取了被反射操作对象的类型。Value提供的方法有很多,几乎大多数方法都有他使用的范围,但是,对于Kind()方法,reflect包中定义的所有反射类型,都可以使用这个方法。

3.2 获取变量对象属性

我们可以通过reflect获取类型为slice的对象变量的属性吗?答案是:不行。

虽然,我们已经通过ValueOf,将slice类型的对象变量与reflect勾搭上了,但是Value提供的一系列方法中,大多数方法,只能针对特定的类型使用,如获取对象属性的方法,前提是这个对象要有属性能够被获取,golang中哪些类型会包含属性了,struct就是其中一个。所以,通过反射获取变量对象属性,是针对于对象变量是struct设计的,如果乱用,在slice,Array中使用获取属性的反射方法,就会panic。反射,是一个比较方便的编程手段,用的好,可以帮助我们减少很多代码量,大大提高API的处理范围;但是用的不好,就会深藏很多panic,如果没有捕获异常,就会导致系统奔溃。

下边来看一个获取对象属性的例子:

type Ref struct {
key string
val string
}
func main() {
ref := Ref{key: "hi", val: "girl"}
iref := reflect.ValueOf(ref)
fmt.Println(iref.Field(0).String())
fmt.Println(iref.Field(1).String())
}
// Output:
//     hi
//     girl


通过反射,我们获取到了struct类型对象变量的值,即使这个值是私有的,照样可以获取。

通过上边的例子,使用反射,获取到了对象变量的属性值。假如在初始化Ref结构体时,使用的是指针呢?获取对象变量的属性还会是这样写吗?答案是:不是。

在reflect包中,Value类提供了很多方法,但是几乎各个方法,都有其适用的对象类型。也就是说,被反射的对象变量是什么类型,就决定过了,就决定了,Value提供的哪些方法可用,哪些方法不可用。如上边获取属性的Field()方法,就只能当被反射的对象是struct类型时,才能调用。如果初始化struct为指针,则需要首先取出指针指向地方的内容,如果指向的struct类型,则才能调用Field()方法。reflect包中,获取指针指向内容的方法时Elem,也可以用Indirect()方法。

下边是反射对象是struct指针时,获取属性的代码示例:

ref := &Ref{key: "hi", val: "girl"}
iref := reflect.ValueOf(ref)
fmt.Println(iref.Elem().Field(0).String())
fmt.Println(iref.Elem().Field(1).String())


注意:Elem方法,也有其适用范围,就是被反射的对象必须是Ptr或者Interface类型。

3.3 获取变量对象方法

想要获取变量对象的方法,前提是,这个被反射的变量对象能够有自己的方法,golang中,处处都是类,所以,除了Interface只让声明,不让实现自己的方法外,其余的类型,基本上都可以有自己的方法。所以,获取变量对象方法方法,适用于大多数变量对象。

下边来一个例子,来实操reflect操作变量对象的方法:

type Ref struct {
key string
val string
}

func (this *Ref) SayHi(str interface{}) {
fmt.Println("reflect say:", str)
}

func main() {
ref := &Ref{key: "hi", val: "girl"}
iref := reflect.ValueOf(ref)
fn := iref.MethodByName("SayHi")
var str = "hello reflect"
var in []reflect.Value
in = append(in, reflect.ValueOf(str))
fn.Call(in)
}


上边例子,实现了reflect调用变量对象的方法。通过上边3个例子,实现了反射访问变量对象类型,属性,方法。

其实反射的作用远远不止上边几个,在使用reflect的过程中,你会发现更多有趣的玩法。

总结:

弄清楚reflect包中Value提供的每一种方法使用的范围,否则你会遇到很多panic
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  golang reflect 反射