Go 里面的接口,绝对是我入坑程序员以来觉得最坑的一个东西了。为什么说它坑,就是怎么看怎么别扭。
说明
Go 中的接口是由使用者来定义的。这和传统的 接口 有点不一样(当然我们在开发的过程中可以根据具体的情况去决定谁用接口)
看一个例子:
// 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 你可以换个名字,比如这样看:
// 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 , 虽然的确是实现的是他,但是我们不能猜,我在改一下:
type Retriever interface {Get(url string) string
}
type InterfaceRetriever interface {Get(url string) string
}
现在你觉得,他实现的是哪一个?是不是看不出来?这怎么可能看的出来实现的是哪一个呢?这就是我觉得 GO 里面比较恶心的地方之一。我们现在来看一下,编辑器为我们的引导:
Retriever interface:
InterfaceRetriever interface:
Retriever struct:
现在可以看到了,两个 interface 之间互通,Retriever struct 也是脚踏两只船。
可是如果这么改之后:
这个时候,InterfaceRetriever interface 实现的是 Retriever interface 接口,Retriever struct 也是实现 Retriever interface 接口。这样,就能说明问题了。Go 里面的接口实现看你实现的是那个方法里面接口,而且是全部实现才算你实现了接口,而且这里面的优先原则是谁先被实现完,谁就是谁爸爸。
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 里面,是包含这个 接口类型和值的。
- 接口变量自带指针
- 接口变量同样采用值传递,几乎不需要使用接口的指针
- 指针接收者实现只能以指针方式使用;值接收者都可以