本博客由 [Pipe](https://github.com/b3log/pipe) 强力驱动

理解 Go interface 的几个要素

interface 是一种类型

type Animal interface {
    GetName() string
}

interface 是一种类型, 使用 type 关键字可以定义一种 interface; interface 一般会有一组方法; 这些方法定义了这个 interface 的行为; 然后如果一个类实现了 interface 的所有方法, 那么我们可以说这个类实现了该 interface。

Go 中存在不带任何方法的 interface ,称作 empty interface, 文章后面会讲到。

interface 变量存储

type Animal interface {
	GetName() string
	SetName(string)
}

type Bird struct{
	Name string
}

func(bird Bird) GetName() string{
	return bird.Name // 1
}

func(bird *Bird) SetName(name string){
	bird.Name = name // 2
}

// 3
func helloBird(animal Animal){
	animal.SetName("bird")
	fmt.Println(animal.GetName())
}

func main(){
	bird := Bird{}
	helloBird(&bird) // 4
}

这段代码 定义了一个 interface: Animal, 然后Bird实现Animal的方法, 执行helloBird(&bird) 即可完成 interface 的类型调用。

func helloBird(animal Animal) 内部, GO 会在对 interface 实现自动转换,这就是 interface 的魔力所在。感兴趣的朋友可以查看Go Data Structures: Interfaces

这里需要注意一点: 传参数时 应该这样: helloBird(&bird)// 4, 而不是: helloBird(bird)// 4, 否则就会报错:

cannot use bird (type Bird) as type Animal in argument to helloBird:
	Bird does not implement Animal (SetName method has pointer receiver)

interface 定义时并没有严格规定实现者的方法 receiver 是个 value receiver 还是 pointer receiver, 但是由于 上面代码中的 SetName 是 pointer,所以需要将&bird 传给helloBird

如果没有SetName(string), 调用时使用helloBird(&bird)或者helloBird(bird)都可以; 这是因为 Go 会把指针进行隐式转换得到 value,但反过来则不行

类型断言

当多种类型都实现一个 interface 时,我们在运行的时候 就会动态的知道 interface 的变量究竟是哪种类型的值,Go 可以使用 comma, ok 的形式做区分 value, ok := em.(T):em 是 interface 类型的变量,T 代表要断言的类型,value 是 interface 变量存储的值,ok 是 bool 类型表示是否为该断言的类型 T。

if t, ok := i.(*S); ok {
    fmt.Println("s implements I", t)
}

ok 是 true 表明 i 存储的是 *S 类型的值,false 则不是,这种区分能力叫 Type assertions (类型断言)。

如果需要区分多种类型,可以使用 switch 断言,更简单直接,这种断言方式只能在 switch 语句中使用。

switch t := i.(type) {
case *S:
    fmt.Println("i store *S", t)
case *R:
    fmt.Println("i store *R", t)
}

相关文章

如果你对 Go 也感兴趣, 可以关注我的公众号

留下你的脚步