面向接口
golang是一门面向接口的语言,没有传统的继承和多态,golang的面向对象仅支持封装(通过定义结构实现,type struct_name struct),而继承和多态是通过接口来实现的。所以golang的接口要比其他语言灵活的多。
1 | type Traversal interface { |
duck typing
- 描述事物的外部行为而非内部结构
- 严格说go属于结构化类型系统,类似duck typing
Go中的duck typing
- 同时能实现多个接口
- 同时具有python, c++的duck typing的灵活性 — 只要实现了某个方法,就能被调用
- 又具有Java的类型检查 — 不是动态语言的运行时检查,也不是静态语言的编译时才检查,而是在实现代码的过程中就知道必须要
¶接口的定义
-
接口由使用者定义,而非传统的为某个被调用者实现某个方法,告诉其他调用者,我实现了该方法,你们可以调用我。
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// main.go
type Retriever interface {
Get(url string) string // 注意:interface中定义函数不需要使用func关键字,会自动识别为函数,因为interface内部本身都是函数
}
func download(r Retriever) string {
return r.Get("www.xxx.com")
}
// 以上为接口的定义
func main() {
var r Retriever // 此处的Retriever是上面的接口对象
r = mock.Retriever{"this is a fack xxx.com"} // 此处可见,接口类型变量可以接受其实现类型的变量
fmt.Println(download(r))
}
// mock包中定义实现接口的方法
package mock
// 实现接口
type Retriever struct { // 注意,结构名要大写,因为是要在本包外被调用
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
// 以上为接口的实现
¶小结
-
接口的实现是隐式的
-
只要实现接口里的方法,不需要指明说 我是实现了某个接口,只要实现了接口的(全部)方法即可
上面的例子中,定义了含有Get方法的接口,并定义了一个使用该接口类型(Retriever类型)的函数,在该函数中调用了接口定义的Get方法
实现接口的时候,只要实现了该接口中定义的方法之后,就能够被download函数使用,虽然该函数接受的是接口类型,但是是能够接受该接口类型的实现类型的。接口类型变量可以接受其实现类型的变量。
-
和Python中的type typing类似的地方在于都是只要实现了某个方法,就能被调用(Python中通过魔法函数实现)。
-
但是要注意,因为接口本身功能的要求,实现时,必须实现接口内定义的所有的类型
-
可以有多个结构实现了同一个接口,一个结构也可以同时实现多个接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21type all struct {
content string
}
func (a all) Post(s string) string{
return "post"
}
func (a all) Get(s string) string {
return "get"
}
func AllMethod(r Retriever, m MyTest) string {
return r.Get("http://www.baidu.com") + " " + m.Post("success")
}
func main() {
m := all{"aaa"}
fmt.Println(AllMethod(m, m))
}
// 结果
get post¶接口的值类型
因为go语言中都是值类型,所以定义变量的时候,值实际是应该有值的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18func main() {
var r Retriever // 此处的Retriever是上面的接口对象
fmt.Printf("%T %v\n",r,r)
r = mock.Retriever{"this is a fack xxx.com"}// 此处可见,接口类型变量可以接受其实现类型的变量
fmt.Printf("%T %v\n",r,r)
r = real.Retriever{
UserAgent: "Mozilla/5.0",
Timeout: time.Minute, // 一旦换行,每个k/v值后需要添加逗号
}
fmt.Printf("%T %v\n",r,r)
fmt.Println(reflect.TypeOf(r))
}
//结果
<nil> <nil>
mock.Retriever {this is a fack xxx.com}
real.Retriever {Mozilla/5.0 1m0s}
real.Retriever1
2
3
4
5
6
7
8
9
10
11
12func (r *Retriever) Get(url string) string {
}
r = &real.Retriever{
UserAgent: "Mozilla/5.0",
Timeout: time.Minute, // 一旦换行,每个k/v值后需要添加逗号
}
fmt.Printf("%T %v\n",r,r)
// 结果
*real.Retriever &{Mozilla/5.0 1m0s}