Go 里面的接口,绝对是我入坑程序员以来觉得最坑的一个东西了。为什么说它坑,就是怎么看怎么别扭。

说明

Go 中的接口是由使用者来定义的。这和传统的 接口 有点不一样(当然我们在开发的过程中可以根据具体的情况去决定谁用接口)

看一个例子:

1
2
3
4
5
6
7
8
9
10
11
// package mooc
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
// package main
type Retriever interface {
Get(url string) string
}

Go 里面比较恶心的是什么呢?就是这里的接口实现,从Java过来的看到这个肯定会比较懵逼。首先必须先生命一点,这里的 Retriever struct 和 Retriever interface 不是必须取名要一致的,我纯粹是为了 这个 struct 要去实现 interface , 然后取名一样的话,我知道我这个 struct 实现的是什么 interface , 这里的 interface 你可以换个名字,比如这样看:

1
2
3
4
5
6
7
8
9
10
11
// package mooc
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
// package main
type InterfaceRetriever interface {
Get(url string) string
}

现在你能看出来 Retriever struct 实现的是哪个 interface 吗?看不出来的好吧?你要是觉得,实现的就是这个 InterfaceRetriever , 虽然的确是实现的是他,但是我们不能猜,我在改一下:

1
2
3
4
5
6
7
type Retriever interface {
Get(url string) string
}

type InterfaceRetriever interface {
Get(url string) string
}

现在你觉得,他实现的是哪一个?是不是看不出来?这怎么可能看的出来实现的是哪一个呢?这就是我觉得 GO 里面比较恶心的地方之一。我们现在来看一下,编辑器为我们的引导:
Retriever interface:
1
InterfaceRetriever interface:
2
Retriever struct:
3

现在可以看到了,两个interface 之间互通,Retriever struct 也是脚踏两只船。

可是如果这么改之后:
4

这个时候,InterfaceRetriever interface 实现的是 Retriever interface 接口, Retriever struct 也是实现 Retriever interface 接口。这样,就能说明问题了。 Go 里面的接口实现看你实现的是那个方法里面接口,而且是全部实现才算你实现了接口,而且这里面的优先原则是谁先被实现完,谁就是谁爸爸。

1
2
3
4
5
6
7
8
9
10
var r Retriever
r = mooc.Retriever{
Contents: "this is mock interface",
}
fmt.Printf("%T %v \n" , r ,r)

switch retriever := r.(type){
case mooc.Retriever:
fmt.Println("mooc Retriever: " , retriever.Contents)
}

从这里可以看到,在接口 Retriever 里面, 也就是这里的 r 里面,是包含这个 接口类型和值的。

  • 接口变量自带指针
  • 接口变量同样采用值传递,几乎不需要使用接口的指针
  • 指针接收者实现只能以指针方式使用;值接收者都可以