Go语言基础之接口
我们学习了结构体(以及其他非结构类型)实现方法。接口是一组方法签名的集合,然后我们可以定义一个结构体实现该接口所有方法。因此,接口就是定义了对象的行为。
例如,结构体Dog可以walk和bark, 如果一个接口声明了walk和bark的方法签名,而Dog实现了walk和bark方法,那么Dog就实现了该接口。
接口的主要工作是仅提供由方法名称,输入参数和返回类型组成的方法签名集合。由类型(例如struct结构体)来声明方法并实现它们。
如果您曾经是面向对象的程序员,您肯定会经常使用implements关键字来实现接口。但是在go中,你没有明确提到一个类型是否实现了一个接口。如果一个类型实现了在接口中定义的签名方法,则称该类型实现该接口。就像说它像鸭子一样走路,像鸭子一样游泳,像鸭子一样嘎嘎叫,那就是鸭子。
Go是一门静态语言,有着严格的静态语言的类型检查。同时GO又引入了动态语言的便利,通过“鸭子类型”的接口来实现动态多态非常的方便。
goroutine
和channel
(后面会讲到)支撑起了GO的高并发模型,而接口类型则是Go的整个类型系统的基石。通过接口,可以实现运行时多态,类型转换,类型断言,方法的动态分派等等功能。
接口
接口类型
在Go语言中接口(interface)是一种类型,一种抽象的类型,用来定义行为(方法)。这句话有两个重点,类型和定义行为。
首先解释定义行为:
接口即一组方法定义的集合,定义了对象的一组行为,就是定义了一些函数,由具体的类型实例实现具体的方法。
换句话说,一个接口就是定义(规范或约束),接口并不会实现这些方法,具体的实现由类实现,实现接口的类必须严格按照接口的声明来实现接口提供的所有功能。接口的作用应该是将定义与实现分离,降低耦合度。
在多人合作开发同一个项目时,接口表示调用者和设计者的一种约定,事先定义好相互调用的接口可以大大提高开发的效率。有了接口,就可以在不影响现有接口声明的情况下,修改接口的内部实现,从而使兼容性问题最小化。
为了保护你的Go语言职业生涯,请牢记接口(interface)是一种类型。
为什么要使用接口
type cat struct {
}func (c cat) speak() {
fmt.Println("喵喵喵")}type dog struct {
}func (d dog) speak() {
fmt.Println("汪汪汪")}func main() {
c := Cat{} fmt.Println("猫:", c.speak()) d := Dog{} fmt.Println("狗:", d.speak())}
上面的代码中定义了猫和狗,然后它们都会叫,你会发现main函数中明显有重复的代码,如果我们后续再加上猪、青蛙等动物的话,我们的代码还会一直重复下去。那我们能不能把它们当成“能叫的动物”来处理呢?
像类似的例子在我们编程过程中会经常遇到:
比如一个网上商城可能使用支付宝、微信、银联等方式去在线支付,我们能不能把它们当成“支付方式”来处理呢?
比如三角形,四边形,圆形都能计算周长和面积,我们能不能把它们当成“图形”来处理呢?
比如销售、行政、程序员都能计算月薪,我们能不能把他们当成“员工”来处理呢?
Go语言中为了解决类似上面的问题,就设计了接口这个概念。接口区别于我们之前所有的具体类型,接口是一种抽象的类型。当你看到一个接口类型的值时,你不知道它是什么,唯一知道的是通过它的方法能做什么。
接口的定义
Go语言提倡面向接口编程。
每个接口由数个方法组成,接口的定义格式如下:
type 接口名称 interface{
方法名1( 参数1, 参数2, ...) (返回值1, 返回值2, ...) 方法名2( 参数2, 参数2, ...) (返回值1, 返回值2, ...) …}
其中:
接口名称:使用
type
将接口定义为自定义的类型名。Go语言的接口在命名时,一般会在单词后面添加er
,如有写操作的接口叫writer
,有字符串功能的接口叫stringer
等。接口名最好要能突出该接口的类型含义。方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。
举个例子:
type writer interface{
write([]byte) error}
当你看到这个接口类型的值时,你不知道它是什么,唯一知道的就是可以通过它的Write方法来做一些事情。