interface
,它是 Go 语言实现抽象的一个非常强大的工具。当向接口变量赋予一个实体类型的时候,接口会存储实体的类型信息,反射就是通过接口的类型信息实现的,反射建立在类型的基础上。int, float64, []int
等等。注意,这个类型是声明时候的类型,不是底层数据类型。MyInt
。itab
由具体类型 _type
以及 interfacetype
组成。_type
表示具体类型,而 interfacetype
则表示具体类型实现的接口类型。eface
,描述的是空接口,不包含任何方法,Go 语言里有的类型都 “实现了”
空接口。iface
,eface
就比较简单了。只维护了一个 _type
字段,表示空接口所承载的具体的实体类型。data
描述了具体的值。Reader
和 Writer
接口:r
的类型是 io.Reader
,注意,这是 r
的静态类型,此时它的动态类型为 nil
,并且它的动态值也是 nil
。r = tty
这一语句,将 r
的动态类型变成 *os.File
,动态值则变成非空,表示打开的文件对象。这时,r 可以用<value, type>
对来表示为: <tty, *os.File>
。fun
所指向的函数只有一个 Read
函数,其实 *os.File
还包含 Write
函数,也就是说 *os.File
其实还实现了 io.Writer
接口。因此下面的断言语句可以执行:r
的静态类型是 io.Reader
,并没有实现 io.Writer
接口。断言能否成功,看 r
的动态类型是否符合要求。<tty, *os.File>
,仅管它和 r
一样,但是 w 可调用的函数取决于它的静态类型 io.Writer
,也就是说它只能有这样的调用形式: w.Write()
。w
的内存形式如下图:r
相比,仅仅是 fun
对应的函数变了:Read -> Write
。empty
是一个空接口,因此所有的类型都实现了它,w 可以直接赋给它,不需要执行断言操作。_type
是类型信息,*data
指向实际类型的实际值,itab
包含实际类型的信息,包括大小、包路径,还包含绑定在类型上的各种方法(图上没有画出方法),补充一下关于 os.File 结构体的图:“伪装”
的 iface 和 eface 结构体。r,w,empty
的动态类型和动态值都一样。不再详细解释了,结合前面的图可以看得非常清晰。reflect.Type
和 reflect.Value
,它们提供很多函数来获取存储在接口里的类型信息。reflect.Type
主要提供关于类型相关的信息,所以它和 _type
关联比较紧密;reflect.Value
则结合 _type
和 data
两者,因此程序员可以获取甚至改变类型的值。TypeOf
函数用来提取一个接口中值的类型信息。由于它的输入参数是一个空的 interface{}
,调用此函数时,实参会先被转化为 interface{}
类型。这样,实参的类型信息、方法集、值信息都存储到 interface{}
变量里了。emptyInterface
和上面提到的 eface
是一回事(字段名略有差异,字段是相同的),并且在不同的源码包:前者在 reflect
包,后者在 runtime
包。 eface.typ
就是动态类型。toType
函数,只是做了一个类型转换:Type
实际上是一个接口,定义了很多方法,用来获取类型相关的各种信息,而 *rtype
实现了 Type
接口。Type
定义了非常多的方法,通过它们可以获取类型的一切信息,大家一定要完整的过一遍上面所有的方法。Type
方法集的倒数第二个方法 common
返回的 rtype
类型,它和上一篇文章讲到的 _type
是一回事,而且源代码里也注释了:两边要保持同步:rtype
这个字段,表示各种类型的公共信息;另外,不同类型包含自己的一些独特的部分。arrayType
和 chanType
都包含 rytpe
,而前者还包含 slice,len 等和数组相关的信息;后者则包含 dir
表示通道方向的信息。Type
接口实现了 String()
函数,满足 fmt.Stringer
接口,因此使用 fmt.Println
打印的时候,输出的是 String()
的结果。另外,fmt.Printf()
函数,如果使用 %T
来作为格式参数,输出的是 reflect.TypeOf
的结果,也就是动态类型。例如:TypeOf
函数,再来看一下 ValueOf
函数。返回值 reflect.Value
表示 interface{}
里存储的实际变量,它能提供实际变量的各种信息。相关的方法常常是需要结合类型信息和值信息。例如,如果要提取一个结构体的字段信息,那就需要用到 _type (具体到这里是指 structType) 类型持有的关于结构体的字段信息、偏移信息,以及 *data
所指向的内容 —— 结构体的实际值。i
转换成 *emptyInterface
类型, 再将它的 typ
字段和 word
字段以及一个标志位字段组装成一个 Value
结构体,而这就是 ValueOf
函数的返回值,它包含类型结构体指针、真实数据的地址、标志位。Value
字段还有很多其他的方法。例如:src/reflect/value.go
去看看源码,搜索 func (v Value)
就能看到。Type()
方法和 Interface()
方法可以打通 interface
、Type
、Value
三者。Type() 方法也可以返回变量的类型信息,与 reflect.TypeOf() 函数等价。Interface() 方法可以将 Value 还原成原来的 interface。TypeOf()
函数返回一个接口,这个接口定义了一系列方法,利用这些方法可以获取关于类型的所有信息; ValueOf()
函数返回一个结构体变量,包含类型信息以及实际值。rtye
实现了 Type
接口,是所有类型的公共部分。emptyface 结构体和 eface 其实是一个东西,而 rtype 其实和 _type 是一个东西,只是一些字段稍微有点差别,比如 emptyface 的 word 字段和 eface 的 data 字段名称不同,但是数据型是一样的。
1.Reflection goes from interface value to reflection object. 2.Reflection goes from reflection object to interface value. 3.To modify a reflection object, the value must be settable.
interface
中的类型和值机制。这可以通过 TypeOf
函数和 ValueOf
函数得到。ValueOf
的返回值通过 Interface()
函数反向转变成 interface
变量。接口型变量
和 反射类型对象
可以相互转化,反射类型对象实际上就是指的前面说的 reflect.Type
和 reflect.Value
。v
不能代表 x
本身,为什么?因为调用 reflect.ValueOf(x)
这一行代码的时候,传入的参数在函数内部只是一个拷贝,是值传递,所以 v
代表的只是 x
的一个拷贝,因此对 v
进行操作是被禁止的。Value
的一个性质,但不是所有的 Value
都是可被设置的。p
还不是代表 x
,p.Elem()
才真正代表 x
,这样就可以真正操作 x
了:Value
必须要 hold 住原变量的地址才行。