golang是一门面向接口的语言,没有传统的继承和多态,golang的面向对象仅支持封装(通过定义结构实现,type struct_name struct),而继承和多态是通过接口来实现的。所以golang的接口要比其他语言灵活的多。
type Traversal interface {
Traverse()
}
func main(){
traversal := getTraversl()
traversal.Traverse()
}
duck typing
- 描述事物的外部行为而非内部结构
- 严格说go属于结构化类型系统,类似duck typing
Go中的duck typing
- 同时能实现多个接口
- 同时具有python, c++的duck typing的灵活性 --- 只要实现了某个方法,就能被调用
- 又具有Java的类型检查 --- 不是动态语言的运行时检查,也不是静态语言的编译时才检查,而是在实现代码的过程中就知道必须要
-
接口由使用者定义,而非传统的为某个被调用者实现某个方法,告诉其他调用者,我实现了该方法,你们可以调用我。
// 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中通过魔法函数实现)。
-
但是要注意,因为接口本身功能的要求,实现时,必须实现接口内定义的所有的类型
-
可以有多个结构实现了同一个接口,一个结构也可以同时实现多个接口
type 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语言中都是值类型,所以定义变量的时候,值实际是应该有值的。
func 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.Retriever
func (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}