Golang将interface作为一种类型,并且不依赖继承,而是以一种类似于duck-typing的实现。所谓duck-typing,是一种动态类型风格:当一个obj走起来像鸭子、游泳起来像鸭子、叫起来也像鸭子,那么它就可以被称为鸭子。
既然Go并没有像C++那样要求主动告诉编译器需要继承哪个父类,那么是如何实现动态类型的呢?(基于Go1.6,1.7及之后版本由于nameOff不方便gdb打印)
首先,interface由两部分组成{tab, data},其中tab保存了接口的元数据,这个很重要。
type iface struct {
tab *itab
data unsafe.Pointer
}
itab中比较重要的有interfacetype及fun[],其中interfacetype保存了该接口需要实现哪些方法,fun[]则保存动态类型是如何实现这些方法的
type itab struct {
inter *interfacetype
_type *_type
link *itab
bad int32
unused int32
fun [1]uintptr // variable sized
}
type interfacetype struct {
typ _type
mhdr []imethod
}
type imethod struct {
name *string
pkgpath *string
_type *_type
}
附:一篇经典论文中的图解
e.g. 唐老鸭的go版本
package main
import (
“fmt”
)
type Duck interface {
GaGaSpeaking()()
OfficialWalking()()
}
type Actor interface {
MakeFun()()
}
type DonaldDuck struct {
height uint
name string
}
func (dd *DonaldDuck) GaGaSpeaking()() {
fmt.Println(“DonaldDuck gaga”)
}
func (dd *DonaldDuck) OfficialWalking()() {
fmt.Println(“DonaldDuck walk”)
}
func (dd *DonaldDuck) MakeFun()() {
fmt.Println(“DonaldDuck make fun”)
}
func main() {
dd := &DonaldDuck{10, "tang lao ya" }
var duck Duck = dd
var actor Actor = dd
duck.GaGaSpeaking()
actor.MakeFun()
dd.OfficialWalking()
}
我们用gdb调试一下
首先,看下结构类型与两个接口的内存关系
(gdb) p dd
$1 = (struct main.DonaldDuck *) 0xc82000e180
(gdb) p duck
$2 = {tab = 0x7ffff7f721c0, data = 0xc82000e180}
(gdb) p actor
$3 = {tab = 0x7ffff7f721f0, data = 0xc82000e180}
可见,duck与actor的data指针都指向dd
然后是Duck接口的方法集:
(gdb) p duck.tab.inter.mhdr.len
$4 = 2
(gdb) p *(duck.tab.inter.mhdr.array[0].name)
$5 = {str = 0x4ff8c0 “GaGaSpeaking”, len = 12}
(gdb) p *(duck.tab.inter.mhdr.array[1].name)
$6 = {str = 0x4ffb30 “OfficialWalking”, len = 15}
以及其动态类型的具体实现(可以看到,都指向了tanglaoya的具体实现)
(gdb) info symbol duck.tab.fun[0]
main.(*DonaldDuck).GaGaSpeaking in section .text of /home/yugui/go/go
(gdb) info symbol duck.tab.fun[1]
main.(*DonaldDuck).OfficialWalking in section .text of /home/yugui/go/go
再看看Actor的方法集:
(gdb) p actor.tab.inter.mhdr.len
$12 = 1
(gdb) p *(actor.tab.inter.mhdr.array[0].name)
$13 = {str = 0x4fcc10 “MakeFun”, len = 7}
及其动态类型的具体实现
(gdb) info symbol actor.tab.fun[0]