interface底层上是分别由两个struct实现:iface和eface。eface表示empty interface,不包含任何方法,iface 表示 non-empty interface,即包含方法的接口。从概念上来讲,iface和eface均由两部分组成:type和value,type表示interface的类型描述,主要提供concrete type相关的信息,value指向interface绑定的具体数据。
//具体类型实例传递给接口称为接口的实例化,这里有个地方值得注意,
//Interface变量默认值为nil,需要初始化后才有意义。
package main
import (
"fmt"
)
type People interface {
Show()
}
type Student struct {
}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
return stu
}
func main() {
var b People
fmt.Println(b)
fmt.Println(b == nil)
}
//out put
//---------------------------
//<nil>
//true
空接口eface结构比较简单,由两个属性构成,一个是类型信息_type,一个是数据信息。
type eface struct {
_type *_type
data unsafe.Pointer
}
其中_type是GO语言中所有类型的公共描述,Go语言几乎所有的数据结构都可以抽象成 _type,是所有类型的公共描述,type负责决定data应该如何解释和操作,type的结构代码如下:
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32 // 类型哈希
tflag tflag
align uint8 // _type作为整体变量存放时的对齐字节数
fieldalign uint8
kind uint8
alg *typeAlg
// gcdata stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte
str nameOff
ptrToThis typeOff // type for pointer to this type, may be zero
}
data表示指向具体的实例数据,由于Go的参数传递规则为值传递,如果希望可以通过interface对实例数据修改,则需要传入指针,此时data指向的是指针的副本,但指针指向的实例地址不变,仍然可以对实例数据产生修改。
type iface struct {
tab *itab
data unsafe.Pointer
}
iface 表示 non-empty interface 的数据结构,非空接口初始化的过程就是初始化一个iface类型的结构,其中data的作用同eface的相同
iface结构中最重要的是itab结构(结构如下),每一个 itab 都占 32 字节的空间。itab可以理解为pair 。itab里面包含了interface的一些关键信息,比如method的具体实现。
// layout of Itab known to compilers
// allocated in non-garbage-collected memory
// Needs to be in sync with
// ../cmd/compile/internal/gc/reflect.go:/^func.dumptypestructs.
type itab struct {
inter *interfacetype // 接口自身的元信息
_type *_type // 具体类型的元信息
link *itab
bad int32
hash int32 // _type里也有一个同样的hash,此处多放一个是为了方便运行接口断言
fun [1]uintptr // 函数指针,指向具体类型所实现的方法
}
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod
}
type imethod struct { //这里的 method 只是一种函数声明的抽象,比如 func Print() error
name nameOff
ityp typeOff
}
其中值得注意的字段
fun个人理解是类似于C++中的虚函数指针的存在,当通过接口调用函数时,实际操作就是s.itab->func()。但不同与C++的虚表之处是,GO是在运行时生成虚表,保障了唯一性,避免了C++中可能存在的同一个接口在不同层次被多次继承实现的等一系列问题,但这里会产生额外的时间消耗。
所以,接口在声明时,本身是空的,但是由结构体实现的接口,即使结构体是nil,但是接口本身并不会是nil
package main
import (
"fmt"
"reflect"
"unsafe"
)
type People interface {
Show()
}
type Student struct{}
func (stu *Student) Show() {
}
func live() People {
var stu *Student
return stu
}
type iface struct {
itab *itab
data unsafe.Pointer
}
type tflag uint8
type nameOff int32
type typeOff int32
type _type struct {
size uintptr
ptrdata uintptr // size of memory prefix holding all pointers
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
// function for comparing objects of this type
// (ptr to object A, ptr to object B) -> ==?
equal func(unsafe.Pointer, unsafe.Pointer) bool
// gcdata stores the GC type data for the garbage collector.
// If the KindGCProg bit is set in kind, gcdata is a GC program.
// Otherwise it is a ptrmask bitmap. See mbitmap.go for details.
gcdata *byte
str nameOff
ptrToThis typeOff
}
type itab struct {
inter *interfacetype // 接口自身的元信息
_type *_type // 具体类型的元信息
link *itab
bad int32
hash int32 // _type里也有一个同样的hash,此处多放一个是为了方便运行接口断言
fun [1]uintptr // 函数指针,指向具体类型所实现的方法
}
type name struct {
bytes *byte
}
type interfacetype struct {
typ _type
pkgpath name
mhdr []imethod
}
type imethod struct { //这里的 method 只是一种函数声明的抽象,比如 func Print() error
name nameOff
ityp typeOff
}
func SmartPrint(i interface{}) {
vValue := reflect.ValueOf(i)
vType := reflect.TypeOf(i)
for i := 0; i < vValue.NumField(); i++ {
fmt.Printf("%s : ", vType.Field(i).Name)
temp := vValue.Field(i)
fmt.Println(temp)
}
}
func main() {
a := live()
var b People
fmt.Printf("A is nil ? %v\n", a == nil)
fmt.Printf("B is nil ? %v\n", b == nil)
p1 := unsafe.Pointer(&a)
p2 := unsafe.Pointer(&b)
fmt.Printf("Iface A info is \n")
SmartPrint(*(*iface)(p1))
fmt.Printf("Iface B info is \n")
SmartPrint(*(*iface)(p2))
if a == nil {
fmt.Println("AAAAAAA")
} else {
fmt.Println("BBBBBBB")
}
}
//output
//--------------------------------------
//A is nil ? false
//B is nil ? true
//Iface A info is
//itab : &{0x10b66e0 0x10b3ea0 0x1279310c 17427248 0 [17524320]}
//data : <nil>
//Iface B info is
//itab : <nil>
//data : <nil>
//BBBBBBB
好好学习,天天向上