interface
Go 语言中 interface 的实现源码主要包括两个核心数据结构:eface 和 iface。
eface
eface 代表空接口 interface{}。它的定义如下:
type eface struct {
_type *_type
data unsafe.Pointer
}
-
_type指向一个_type结构体,描述了该接口值的动态类型信息。 -
data是一个指针,指向该接口值的实际数据。
iface
iface 代表非空接口,比如 io.Reader。它的定义如下:
type iface struct {
tab *itab
data unsafe.Pointer
}
tab指向一个itab结构体,描述了该接口的元数据信息。data同 eface,指向实际数据。
itab
itab 结构体定义如下:
type itab struct {
inter *interfacetype
_type *_type
hash uint32
_ [4]byte
fun [1]uintptr
}
inter指向该接口自身的元数据。_type指向实际数据的类型信息。hash是类型的哈希值,用于快速比较。fun是一个函数指针数组,存储了该类型实现的接口方法。
当将一个具体类型的值赋给接口值时,编译器会根据该类型实现的方法生成一个 itab,并将其与实际数据的 _type 信息一起存储在 iface 中。这样在运行时,通过 iface.tab 就可以找到该值实现的接口方法集。
通过这种方式,Go 语言实现了接口的动态分发。当调用接口值的方法时,运行时会根据 iface.tab 找到正确的方法实现并执行。这种延迟绑定让不同类型的值可以被视为同一接口类型,实现了面向接口编程。
空接口 interface{} 的使用
var data *byte{}
var in interface{}
fmt.Println(data, data == nil) // <nil> true
fmt.Println(in, in == nil) // <nil> true
in = data
fmt.Println(in, in == nil) // <nil> false
解析: 空接口 interface{} 可以存储任何类型的值,包括 nil。当将 nil 赋值给空接口时,其底层数据仍为 nil,但其类型信息已经存在,因此 in == nil 为 false
接口值比较
// 1. type 和 data 都相等
type ProfileInt interface {}
func main() {
var p1, p2 ProfileInt = Profile{"iswbm"}, Profile{"iswbm"}
var p3, p4 ProfileInt = &Profile{"iswbm"}, &Profile{"iswbm"}
fmt.Printf("p1 --> type: %T, data: %v \n", p1, p1)
fmt.Printf("p2 --> type: %T, data: %v \n", p2, p2)
fmt.Println(p1 == p2) // true
fmt.Printf("p3 --> type: %T, data: %p \n", p3, p3)
fmt.Printf("p4 --> type: %T, data: %p \n", p4, p4)
fmt.Println(p3 == p4) // false
}
// 2. 两个都是nil
type ProfileInt interface {}
func main() {
var p1, p2 ProfileInt
fmt.Println(p1==p2) // true
}
// 3.interface 与 非interface 比较
func main() {
var a string = "iswbm"
var b interface{} = "iswbm"
fmt.Println(a==b) // true
}
func main() {
var a *string = nil
var b interface{} = a
fmt.Println(b==nil) // false
}
解析: 在 Go 中,接口值的比较并不是比较其动态值,而是比较其动态类型和动态值。上例中 x和 y 的动态类型相同,但动态值不同(不同的内存地址),因此比较结果为 false。
接口新概念(Go 1.18+)
Go 1.18 引入了一些新的接口相关概念:
- Type Set(类型集合): 接口代表的是类型的集合,而不再是方法集合。
- Specific Type(特定类型): 接口可以包含具体的类型,如
interface{int, string}。 - Structural Type(结构类型): 接口可以包含近似类型,如
interface{~string}。
编译期间检查接口实现
type CommonResponseIface interface {
GetCode() int
GetMessage() string
MergeMessage()
}
// 编译期间检查接口实现
var _ CommonResponseIface = (*CommonResponse)(nil)
type CommonResponse struct {
Code int `json:"code"` // 状态码 枚举: 200正常 500错误
Data interface{} `json:"data"` // 数据包
Message string `json:"message"` // 消息。接口返回格式不统一,这边做个兼容
Msg string `json:"msg"` // 消息。接口返回格式不统一,这边做个兼容
}
func (c *CommonResponse) GetCode() int {
return c.Code
}
func (c *CommonResponse) GetMessage() string {
if c.Message != "" {
return c.Message
}
return c.Msg
}
func (c *CommonResponse) MergeMessage() {
if c.Message == "" {
c.Message = c.Msg
}
}