go语言中有两种类型的接口,一种是不带方法的接口,底层通过runtime.eface实现(empty face);另一种是带有一组方法的接口,底层通过runtime.iface实现。
runtime.eface源码如下:
type eface struct {
_type *_type //指向类型的指针
data unsafe.Point //指向底层数据
}
type _type struct {
size uintptr // type size ,size 字段存储了类型占用的内存空间,为内存空间的分配提供信息;
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32 // hash of type; avoids computation in hash tables, hash 字段能够帮助我们快速确定类型是否相等;
tflag tflag // extra type information flags
align uint8 // alignment of variable with this type
fieldalign uint8 // alignment of struct field with this type
kind uint8 // enumeration for C
alg *typeAlg // algorithm table
gcdata *byte // garbage collection data
str nameOff // string form
ptrToThis typeOff // type for pointer to this type, may be zero
}
runtime.iface源码如下:
type iface struct {
tabe *itabe
data unsafe.Point //指向底层数据
}
type itab struct {
inter *interfacetype
_type *_type
link *itab
bad int32
inhash int32 // has this itab been added to hash?
fun [1]uintptr // variable sized 是一个动态大小的数组,它是一个用于动态派发的虚函数表,存储了一组函数指针
}
interface与nil比较
Go 语言的接口类型不是任意类型
package main
type TestStruct struct{}
func IfEqualNil0(v *TestStruct) bool {
return v == nil
}
func IfEqualNil1(v interface{}) bool {
return v == nil
}
func IfEqualNil2(v1 *TestStruct, v2 interface{}) bool {
return v1 == v2
}
func IfEqualNil3(v1 interface{}, v2 interface{}) bool {
return v1 == v2
}
func main() {
var a *TestStruct
fmt.Println(IfEqualNil0(a)) // true
fmt.Println(IfEqualNil1(a)) // false
fmt.Println(IfEqualNil2(a, nil)) // false
fmt.Println(IfEqualNil3(a, nil)) // false
}
$ go run main.go
true
false
false
false
结果解析: 两个interface 类型变量比较,只有变量的两个指针都相等, 两个变量才相等。一个interface{}类型的变量包含了2个指针,一个 _type *_type 指向类型的指针,另外一个data unsafe.Point 指向底层数据。调用 IfEqualNil1函数时发生了隐式的类型转换,除了向方法传入参数之外,变量的赋值也会触发隐式类型转换。在类型转换时,*TestStruct 类型会转换成 interface{} 类型,转换后的变量不仅包含转换前的变量,还包含变量的类型信息 TestStruct。