接口与实现 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package main import "fmt" type Car interface { Run() } type Tesla struct { Name string } func (t Tesla) Run() { fmt.Println(t.Name + " is Running...") } func main() { var c Car = Tesla{Name: "Tesla"} fmt.Println(c) }
18行 c这个变量是Car类型的值。下面看一下源代码runtime/runtime2.go
1 2 3 4 5 6 7 8 9 10 11 12 type iface struct { tab *itab data unsafe.Pointer } type itab struct { inter *interfacetype _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. }
3行 data的指针类型,才是指向上面的Tesla的类型。
7行 接口类型
8行 接口现在装载具体的值的类型(Tesla类型)
11行 就是具体类型实现的哪些方法
类型转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package main import "fmt" type Car interface { Run() } type Bike interface { Run() } type Tesla struct { Name string } func (t Tesla) Run() { fmt.Println(t.Name + " is Running...") } func main() { var c Car = Tesla{Name: "Tesla"} fmt.Println(c) b := c.(Bike) fmt.Println(b) switch c.(type) { case Bike: fmt.Println("Bike interface") } }
打印结果
Bike interface
因为c是Tesla类型,Tesla类型实现了Car的接口类型,而Bike接口也相同的方法,Tesla可以也可以转换为Bike接口,所以可以打印出来Bike interface。
go build -gcflags -S main.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 main.Tesla.Run STEXT size=160 args=0x10 locals=0x48 funcid=0x0 align=0x0 0x0000 00000 (/menu8/main.go:17) TEXT main.Tesla.Run(SB), ABIInternal, $80-16 ... main.(*Tesla).Run STEXT dupok size=192 args=0x8 locals=0x48 funcid=0x15 align=0x0 0x0000 00000 (<autogenerated>:1) TEXT main.(*Tesla).Run(SB), DUPOK|WRAPPER|ABIInternal, $80-8 ... 0x002c 00044 (/menu8/main.go:18) STP (ZR, ZR), main..autotmp_10-16(SP) 0x0030 00048 (/menu8/main.go:18) MOVD ZR, R0 0x0034 00052 (/menu8/main.go:18) MOVD $go.string." is Running..."(SB), R3 0x003c 00060 (/menu8/main.go:18) MOVD $14, R4 0x0040 00064 (/menu8/main.go:18) PCDATA $1, $1 0x0040 00064 (/menu8/main.go:18) CALL runtime.concatstring2(SB) 0x0044 00068 (/menu8/main.go:18) CALL runtime.convTstring(SB) 0x0048 00072 (/menu8/main.go:18) MOVD $type.string(SB), R5 0x0050 00080 (/menu8/main.go:18) MOVD R5, main..autotmp_10-16(SP) 0x0054 00084 (/menu8/main.go:18) MOVD R0, main..autotmp_10-8(SP)
通过汇编,可以看到,我们创建了Tesla{Name: “Tesla”},同时,编译器也给我们创建了一个指针的Run()方法,调用的逻辑,也是18行,和我们结构体的逻辑是一样的。代码如下:
1 2 3 func (t *Tesla) Run() { fmt.Println(t.Name + " is Running...") }
这样就导致,我们在main函数中,下面的方式,也可以创建成功的
1 2 var c Car = &Tesla{Name: "Tesla"} fmt.Println(c)
如果你只有结构体指针的实现方法,那么编译器,不会给你增加结构体的方法
空接口 runtime/runtime2.go
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 type eface struct { _type *_type data unsafe.Pointer } type _type struct { size uintptr ptrdata uintptr hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte str nameOff ptrToThis typeOff }
相比 iface,eface 就比较简单了。只维护了一个 _type 字段,表示空接口所承载的具体的实体类型。data 描述了具体的值。多用作形参。
如何学习Go语言微服务,快速步入架构师
添加微信
公众号更多内容