go 语言中反射的三大原则
反射是go语言中一个非常重要的特性,go语言圣经是这样定义反射的:
go语言提供了一种机制在运行时更新变量和检查他们的值、调用他们的方法,但是在编译的时候并不知道这些变量的具体类型,这称为反射机制。
通俗来讲:就是说,我们编写的go代码,在程序编译的时候,并不知道某个变量的具体类型,但是我们可以通过在运行时利用反射的机制,来获取这个变量的类型,以及其具体的值。
那么,go语言中就提供了实现这样一个机制的一系列方法,而这些方法在reflect
包中。
首先,我们来介绍一些go中reflect
包常见的一些方法,以及使用他们的方式:
reflect.ValueOf()
获取输入参数接口中的数据的值reflect.TypeOf()
获取输入参数接口中值的类型reflect.TypeOf().Kind()
用来获取值的具体类型
-reflect.ValueOf().Field(int)
获取结构体中的第几个值reflect.FieldByIndex([]int{0,1})
层次取值reflect.ValueOf().Elem()
获取原始可操作的数据
在使用上述方法之前,我们必须先了解一些基本的知识点
- 1、空接口: go语言中接口是一种类型,而空接口类型可以保存任何值。
- 2、interface类型特性:interface类型有个(value,type)对,而反射就是检查interface的这个(value, type)对的。具体一点说就是Go提供一组方法提取interface的value提供另一组方法提取interface的type.
- 3、断言:格式:
x.(T)
: 判断x是否是T类型,一般,x是某一空接口类型,而T是某一具体类型。如果这个检查成功了,类型断言的结果是x的动态值,当然它的类型是T。换句话说,具体类型的类型断言从它的操作对象中获得具体的值。x.(type) 获取x的类型[把一个接口断言成特定的类型].
code01 : 反射第一定律:反射可以将Interface类型变量转换成反射对象
type Animal struct {
Name string
}
type Dog struct {
Animal
Age int
}
func test01() {
/**
反射第一定律:反射可以将interface 类型变量转换城反射对象
*/
dog := Dog{Animal{"hello"}, 18}
func(i interface{}) {
// interface 类型有个(value,type)对
t := reflect.TypeOf(i)
v := reflect.ValueOf(i)
fmt.Println(v, t)
}(dog)
}
code02 : 反射第二定律:反射可以将反射对象还原成interface对象
func ReflectDemo() {
var x float64 = 10.9
t := reflect.TypeOf(x) //获取x的Type
v := reflect.ValueOf(x) //获取x的Value
var y float64 = v.Interface().(float64) //v通过Interface() 函数转成interface{} 对象,interface{}对象通过断言获取float64类型具体的值
fmt.Println(y)
}
code03 : 反射的第三定律:反射对象可以修改,Value值必须是可以设置的
func test04() {
/*
反射第三定律:反射对象可修改,value值必须是可设置的
*/
x := 3.4
v := reflect.ValueOf(&x) //注意这里必须取得是x的地址,这样才可以对x进行修改
v.Elem().SetFloat(9.9)
fmt.Println(v) //此时v是x的地址的包装Value
y := v.Elem()
fmt.Println(y) //此时y对应的是x的真实值
}
*注意:下面这段程序会panic:
func test03() {
/*
反射第三定律:反射对象可修改,value值必须是可设置的
x -> Value -> interface{}
*/
x := 3.4
v := reflect.ValueOf(x)
v.SetFloat(20) //panic, v是不可修改的,
/*
传入 reflect.ValueOf() 函数的其实是 x 的值,而非 x 本身。即通过 v 修改其值是无法影响 x 的,所以会报错。
如果构建 v 时使用 x 的地址就可实现修改了,但此时 v 代表的是指针地址,我们要设置的是指针所指向的内容,也即我们想要修改的是 *v 。 那怎么通过 v 修改 x 的值呢?
reflect.Value 提供了 Elem() 方法,可以获得指针指向的 value 。
*/
}
总结:个人理解,对于反射和断言,我们有时候会将其方法记混乱,那么我们如何来理解会更方便呢?比较直观的理解可以是这样:对于一个普通的变量x, 可以想象其值得包装类为Value, 其类型得包装类为Type, 那么,又因为interface{} 类型包含一个值得value,type对,所以,其最上层包装为interface{} , 所以我们用的函数就是将他们之间进行转换。
上面的内容比较基础,具体的可以参考其他较为详细的书籍,冲!