在Go中经常使用的interface
结构到底是怎样的?我们都知道interface
是实现duck typing
重要的数据结构。实现duck typing
必须包含:类型信息和数据信息。interface
结构体也应该包含这两项信息。下面先看一个例子:
package main
import "fmt"
import "unsafe"
func main() {
a := 1
fmt.Println(unsafe.Sizeof(&a)) // 输出:8
size := unsafe.Sizeof(interface{}(nil))
fmt.Println(size) // 输出:16
}
通过上面的例子我们能知道,一个指针的大小是8个字节,而一个interface
的大小是16个字节。现在有理由怀疑interface
的16个字节包含两个8字节的指针成员。下面看一个例子:
package main
import "fmt"
import "unsafe"
type name interface {
show()
}
type jack struct {
name string
}
func (j jack) show() {
fmt.Println(j.name)
}
func printValue(n *name) {
valuePtr := uintptr(unsafe.Pointer(n)) + uintptr(unsafe.Sizeof(&n))
value := (**jack)(unsafe.Pointer(valuePtr))
fmt.Printf("value: %v\n", (**value).name)
}
func main() {
var j name = jack {
name: "jack",
}
printValue(&j) // 输出:value: jack
}
通过上面的例子可以看到,interface
的9-16
这8个字节保存的是一个指针
,指向结构体jack
的内存地址。而interface
的前8个字节保存的是一个itable
结构体的指针,itable
包含有:类型信息和类型关联的方法(这里没办法通过Go的方法hack进去)。下面看一个关于类型例子:
package main
import "fmt"
type name interface {
show()
}
type jack struct {
name string
}
func (j jack) show() {
fmt.Println(j.name)
}
func main() {
var n name = nil
var jn name = (*jack)(nil)
fmt.Println(n == jn) // false
fmt.Println(n == name(nil)) // true
fmt.Println(n == nil) // true
fmt.Println(jn == nil) // false
}
从上面的例子中,可以发现interface
变量是否相等,必须是类型
和值
都相等才行。var n name = nil
表达式,n
的类型为空,值为nil
;与nil
作比较时,只比较值
,所以两者相等。而var jn name = (*jack)(nil)
表达式,jn
的类型为*jack
,值为nil
;与nil
比较,类型不匹配,所以两者不相等。