广义上来讲,反射是指计算机程序在运行时(run time)可以访问、检测和修改它本身状态或行为的一种能力。也即是说,反射就是程序在运行的时候能够"观察"并且修改自己的行为。 Go语言不是严格的面向对象的语言,虽然它也能够通过接口、结构体、实现接口的方法三者在某种程度上实现面向对象的一些特性,但Go语言的反射机制不像Java的反射机制那样。 Java反射机制实现的功能是:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和查看并修改其属性。 Go语言的反射机制提供了在运行时更新变量和检查它们的值,调用他们的方法,但是在编译时并不知道这些变量的具体类型。这是因为Go语言中没有统一的面向对象编程的定义,对象就是简单的一个值或者变量。
这是Go语言的场景,其他语言可能还包括动态代理什么的。
我们前面的博文介绍过Go语言的接口,它是Go语言实现抽象的一个非常强大的工具。当向接口变量赋予一个实体类型的时候,接口会储存实体的类型信息,反射就是通过接口的类型信息实现的,反射建立在类型的基础上。
Go语言在reflect
包里定义了各种类型,实现了反射的各种函数,通过它们可以在运行时检测类型的信息、改变类型的值。
Go语言中,每一个变量都有一个静态类型,在编译阶段就已经确定,比如int
、float64
、[]int
等。但这个类型是声明类型不是底层数据类型。
下面这个例子来自Go官方博客
type MyInt int
var i int
var j MyInt
变量i
和j
的底层实现都是int
型,但它们逻辑上是不同的静态类型(i
的静态类型是int
,但j
的静态类型是MyInt
),除非进行类型转换,否则两者不能使用等号做比较。
Go语言中反射主要和interface
类型相关,interface
类型底层实现如下,其中iface
对应非空接口,eface
对应空接口。
type itab struct {
inter *interfacetype
_type *_type
link *itab
hash unit32
bad bool
inhasn bool
unused [2] byte
fun [1] unintptr
}
type iface strcut {
tab *itab
data unsafe.Pointer
}
type eface strcut {
_type *_type
data unsafe.Pointer
}
eface
和iface
相比,只维护了iface
所有字段中的一个_type
字段和data
字段,表示空接口所承载的具体的实体类型,data
是一个指针,指向具体的值。
获取到_type
和data
的内容,就实现了反射。
reflect
包中定义了一个接口和一个结构体,即reflect.Type
和reflect.Value
,两者提供很多函数来获取存储在接口里的类型信息。
reflect.Type
主要提供关于类型相关的信息,所以它和_type
关联比较紧密
reflect.Value
则结合_type
和data
两者,因此程序员可以通过它获取甚至改变类型的值。
reflect
包中提供了两个基本的关于反射的函数来获取上述的接口和结构体,分别是TypeOf
和ValueOf
,它们的功能如其名,获取type信息和value信息:
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
Reflection goes from interface value to the reflection object
.Reflection goes from reflection object to interface value
.To modify a reflection object, the value must be settable
.直译如下:
前两者表达了对称性,后两者则是反射能实现的一种条件。
第一条:反射可以检测interface的一个具体值(对象)中的类型和值,通过前述TypeOf
和ValueOf
得到。
第二条:将类型和值封装成一个inteface的一个具体值(对选哪个)
第三条:对反射对象值的修改应当能作用到原值。
第三条举一个具体的例子,以帮助理解。
package main
import (
"fmt"
"reflect"
)
func main() {
i := 1
v := reflect.ValueOf(i)
v.SetInt(2)
fmt.Println(i)
}
运行如上代码,会panic
报错如下,因为传参数进入ValueOf
时是复制的i
的一份值,并不是地址值:
panic: reflect: reflect.Value.SetInt using unaddressable value
goroutine 1 [running]:
reflect.flag.mustBeAssignableSlow(0x82)
D:/Go/src/reflect/value.go:260 +0x146
reflect.flag.mustBeAssignable(...)
D:/Go/src/reflect/value.go:247
reflect.Value.SetInt(0xb30920, 0xbdeb68, 0x82, 0x2)
D:/Go/src/reflect/value.go:1633 +0x45
main.main()
D:/GoProject/main/test.go:11 +0xc5
Process finished with exit code 2
package main
import (
"fmt"
"reflect"
)
func main() {
i := 1
v := reflect.ValueOf(&i)
v.SetInt(2)
fmt.Println(i)
}
传地址还不对,报错还是一样,这是因为v
并代表是i
,v.Elem()
才真正代表i
package main
import (
"fmt"
"reflect"
)
func main() {
i := 1
v := reflect.ValueOf(&i)
v1 := v.Elem()
v1.SetInt(2)
fmt.Println(i)
fmt.Println("type of v:", v.Type())
fmt.Println("Settability of v", v.CanSet())
fmt.Println("type of v:", v1.Type())
fmt.Println("Settability of v", v1.CanSet())
}
运行结果:
2
type of v: *int
Settability of v false
type of v: int
Settability of v true
IDE的代码自动补全,对象序列化(json函数库),fmt相关函数的实现,ORM(对象关系应谁的实现)等等。
Go程序设计语言-机械工业出版社