携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
1、前言
对于没有强类型语言开发经验的,之前没有怎么接触过,对于反射这个概念确实感觉挺困惑的。但是不要紧,在读完本文,您将对反射有一个清晰的理解。
2、什么是反射
反射(reflection)是程序在运行时能够检查变量、变量值和变量类型的能力,而不需要在编译时就知道这些变量的具体类型。
go中的反射也是这种作用,可以在程序运行期间,获取变量的类型与值得信息,然后进行访问或者修改。go语言中,内置了reflect包,用来获取一个变量的类型(Type)与值(Value),对应的方法分别为reflect.TypeOf()和reflect.ValueOf()。
3、为何需要反射
有时候我们需要编写一个函数能够处理一类并不满足普通公共接口的类型的值,也可能是因为不确定多个类型中的其中一个,或者是在我们设计该函数的时候这些类型还不存在。
比如fmt.Sprint 函数提供的字符串格式化处理逻辑,他可以用来对任意类型的值格式化并打印,甚至支持用户自定义的类型,如果没有反射机制:
func Sprint(s interface{}) string { //interface{}可以当做泛型
switch t := s.(type) { //类型断言
case string:
return t
case int:
return strconv.Itoa(t)
// int8 int64 ...多种类型
default:
return "???"
}
}
上述代码中,我们没有方法来一一列举出所有的类型,这就是我们为何需要反射的原因。
3、反射的作用
- 在编写不定传参类型函数的时候,或传入类型过多时
- 不确定调用哪个函数,需要根据某些条件来动态执行
4、反射三定律
为了引出interface,之所以说interface是想说interface类型有个(type, value)对,而反射就是检查interface这个(type, value)对的。
简单点说就是Go提供一组方法提取interface的value,提供另一组方法提取interface的type。
4.1、反射第一定律:反射可以将interface类型变量转换成反射对象
反射是针对interface类型变量的,其中TypeOf和ValueOf接受的参数都是interface{}类型的,也就是变量被转成interface传入的。
4.2、反射第二定律:反射可以将反射对象还原成interface对象
反射对象与interface对象是可以互相转化的。
var x int64 = 1
v := reflect.ValueOf(x)
var y int64 = v.Interface().(int64)
fmt.Println(y)
对象x转换成反射对象v,v又通过Interface接口转换成interface对象,interface对象通过.(int64)类型断言获取int64类型的值。
4.3、反射第三定律:反射对象可修改,但value值必须是可写的
这是一个不起作用的示例,原因是这v是不可设置的,反射对象是否可修改取决于其所存储的值,函数传参是传值,下面示例中,传入reflect.ValueOf()函数的其实是x的值,而非x本身,即通过v修改其值是无法影响x的,也是无效的修改,所以golang会报错。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.Elem().SetFloat(7.1)
fmt.Println("x :", v.Elem().Interface()) //x : 7.1
如果构建v时使用x的地址就可以实现修改了,但此时v代表的是指针地址,我们要设置的是指针所指向的内容,也即我们想要修改的*v。
reflect.Value提供了Elem()方法,可以获得指针指向的Value。
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.