接口与实现

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语言微服务,快速步入架构师

从0到Go语言微服务架构师-海报 从0到Go语言微服务架构师
添加微信 公众号更多内容
wechat gzh