写在前面
反射机制是一个很重要的内容,当我们写框架的时候,要想要松耦合,高复用,那么就有很多地方都需要用到反射,可谓是中高级程序员必须掌握的知识点
很多后台语言都有反射机制,但它们的使用原理大多都是一样的
各语言不同的地方,大致就是代码实现方式不一致罢了
其根本,都是从变量得到反射对象,再由反射对象去操作原变量
好了,步入正题
什么是反射
我就用一句话来概括吧
使用反射,可以让我们在程序运行时对任意类型的对象进行操作
注意操作这两个字,操作是指:可以获取对象的信息、改变对象的值、调用对象的方法、甚至是创建一个对象
说到这你可能有点困惑,我们在编写代码的时候不就已经把该实例化的象进行了实例化,该调用的方法都调用了嘛?为什么写程序的时候不调用方法,偏要在运行时去进行这些操作?
其实问题就在这里,如果我们在写程序的时候,一切的对象与方法都能够确定了,那还要反射做什么?
正是因为我们在写程序的时候,要想写一些“万能程序”,用于降低代码的耦合度,所以我们才需要反射,用于处理一些未知的对象
想想,当我们写一个方法,不管别人往我们这个方法内传入什么样的参数,最后我们的函数都能给别人所需要的内容。是不是感觉很牛逼?
反射的使用原理
我这里主要说使用反射的原理,并不是刨析反射的底层原理,有兴趣想要探索原理的读者大人,可以去看看go的reflect包源码
先给你们上个图,看懂这个关系图,后面的文字基本也就可以不看了
没看懂没关系,稍微解释就能明白~~
intEmployeereflect.TypeOf()Typereflect.ValueOf()Value
reflect.Typereflect.ValueTypeValue
弄明白了这个道理,那一切都将变得简单
reflect
反射的注意事项与细节
TypeKind
TypeKindTypeKind
TypeKindintTypeKindint
TypeKind
我们来看个实际案例
func main() {
var emp Employee
emp = Employee{
Name: "naonao",
Age: 99,
}
rVal := reflect.ValueOf(emp)
log.Printf("Kind is %v ,Type is %v",
rVal.Kind(),
rVal.Type())
// Kind is struct ,Type is main.Employee
}
复制代码
KindstructType包名.Employee
reflect.Value
interface{}reflect.ValueValue
用个表达式来表示,就如下所示
变量<----->interface{}<----->reflect.Value
变量Value
下面我们再说如何用代码实现转换
如何使用反射获取变量本身的值?
reflect.ValueOf()reflect.Value
var num = 1
rVal := reflect.ValueOf(num)
log.Printf("num is %v", num + rVal)
复制代码
invalid operation: num + rVal (mismatched types int and reflect.Value)
rValreflect.Valueint
那怎样才能获得它本身的值呢?
var num intreflectreflect.ValueOf(num).Int()
floatreflect.ValueOf(num).float()
reflect
也就是上面说的,利用空接口进行中转,再利用断言进行类型转换,可以看如下代码示例
// Employee 员工
type Employee struct {
Name string
Age int
}
func main() {
emp := &Employee{
Name: "naonao",
Age: 99,
}
reflectPrint(emp)
}
func reflectPrint(v interface{}) {
rVal := reflect.ValueOf(v) // 获取reflect.Value
iV := rVal.Interface() // 利用空接口进行中转
empVal, ok := iV.(*Employee) // 利用断言转换
if ok {
// 如果成功转换则打印结构体
log.Print(empVal)
}
}
复制代码
swithreflect
通过反射来修改变量
先来看看代码如何实现
func main() {
var num = 1
modifyValue(&num)// 传递地址
log.Printf("num is %v", num)// num is 20
}
func modifyValue(i interface{}) {
rVal := reflect.ValueOf(i)
rVal.Elem().SetInt(20)
}
复制代码
细心的你肯定发现了一点异常,函数接收的参数不再是值了,而是接受了一个指针地址
Elem()SetInt()
reflect
Elem()Elem()
总不可能连值都没拿到手,就想着去改值吧?
如何理解reflect.Value.Elem()
Elem()
num := 1
prt *int := &num // 获取num的指针地址
num2 := *ptr // 从指针处取值
复制代码
因为我们传递了一个地址,所以我们要先拿到这个地址的指针,再通过指针去取得所对应的值
reflect
写在最后
reflect