GO 接口

97 阅读2分钟

【知识碎片】

  • 接口实现:类型A的方法集合是接口 I 的超集,A类型就实现了I接口;所有类型都实现了空接口类型interface{}
  • 接口方法名需具名,其他参数列表类型、返回值类型不用具名,但多个接口嵌入时,方法名如果相同,那么函数签名也必须一样,不能出现类似java 重载的用法。
  • 可以用非导出方法。
  • 实现接口的类型,可以作为该接口类型变量的右值,(泛型之外的具有“泛型”能力的语法元素)
  • 类型断言 comma, ok 和普通:= 结果的区别。
  • 接口的实现,不是显示的 而是通过实现方法 隐式的将类型实现接口

接口类型 在Go运行时的表示

静态特性

接口类型变量的静态类型体现了接口的静态特性,在运行时接口变量右值的真实类型就是接口的动态类型(类似java “多态” ,用父类的类型声明变量可实例化子类的类型)

动态特性

接口类型变量内部表示


// $GOROOT/src/runtime/runtime2.go
//非空接口类型
type iface struct {
    tab  *itab
    data unsafe.Pointer
}

// $GOROOT/src/runtime/runtime2.go
type itab struct {
    inter *interfacetype
    _type *_type // 同空接口中的_type一样
    hash  uint32 // copy of _type.hash. Used for type switches.
    _     [4]byte
    fun   [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.
}

//空接口
type eface struct {
    _type *_type
    data  unsafe.Pointer
}

判断接口变量是否相等

  • _type (空接口就是 tab._type)
  • data所指想内存空间中的值,不是判断data本身

所以接口类型变量的nil _type是有实际地址的 ,nil 的type是0x0,nil也是类型,所有类型都是实现了interface{} 所以都有(_type,data)

go 语言每种类型都有 _type信息


// $GOROOT/src/runtime/print.go
func printeface(e eface) {
    print("(", e._type, ",", e.data, ")")
}

func printiface(i iface) {
    print("(", i.tab, ",", i.data, ")")
}

非空接口和空接口类型变量比较

虽然静态类型不同,但动态类型的同类接口类型变量的 _type/tab 信息是相同的,所以才可以出现相等的情况

var eif interface{} = T(5) 
var err error = T(5)

接口类型的装箱(boxing)原理

装箱后,原变量就和赋值后的接口变量类型没有关系了。 装箱实际上就是 创建_type和data 并创建相关内存,拷贝数据。