go语言的接口
简介
- 接口是双方规定的一种合作协议,接口实现者不需要关心接口会被怎样使用,调用者不需要关心接口的实现细节。接口是一种类型。也是一种抽象结构。不会暴露所含数据的格式、类型及结构。比如只要一台洗衣机有洗衣服的功能和甩干的功能,我们就称为洗衣机,不关心属性(数据),只关心行为(方法)。
- 接口和其他动态语言的鸭子模型有密切关系。比如说
python
、javascript
。任何类型,只要实现了该接口中的方法集,那么就属于这个类型。 - 每个接口由数个方法组成。
接口的定义
格式:
1 | type 接口类型名 interface { |
- 接口类型名:使用
type
将接口定义为自定义的类型名 - 方法名:当方法名首字母是大写时,且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包之外的代码访问。
- 参数列表、返回值列表:参数列表和返回值列表的参数变量名可以被忽略
示例:
1 | // 可以吃 |
看到这个接口时,只知道他有一个eat()
方法。并不知道谁实现了这些接口,也不知道是怎么实现的这些这个方法的。
实现接口的条件
- 接口的方法与实现接口的类型方法格式一致
- 接口的方法与实现接口的类型方法数目保持一致。即接口中的所有方法均被实现。
1 | package main |
可以看出来,duckChicken
可以直接调用GaGa
和YouYong
。他并不知道这两个方法内部怎么实现的。
值类型接收者和指针型接收者实现接口
1 | package main |
可以发现,使用值类型实现接口后,不管dog结构体实例化是指针型还是值类型。都可以赋值给该接口变量。因为go语言内部有对指针类型变量求值的语法糖。
但是指针型实现接口后,只能指针型变量赋值给接口变量。
类型与接口的关系
类型与接口有一对多和多对多的关系。
一(类型)对多(接口):
1 | package main |
可以看出两个函数完全独立,完全不知道对方的存在,也不知道使用自己的接口是socket使用的
多(类型)对一(接口)
接口的方法不一定要一个结构体类型完全实现,接口的方法可以通过结构体嵌入实现。
1 | package main |
可以看出,服务接口下一个服务启动功能和日志输出功能,但是游戏服务类型并没有实现日志输出功能,而是间接通过内嵌日志类型来实现,日志类型实现了日志输出功能,所有游戏服务类型实现的接口可以直接使用日志输出功能。
接口的嵌套组合
不仅类型与类型之间可以嵌套,接口与接口之间也可以嵌套。
1 | package main |
空接口
空接口是接口类型的特殊形式,空接口没有任何方法。因此任何类型都无须实现,从实现的角度来看。任何值都满足这个接口的需求。因此空接口类型可以保存任何值,也可以从中取值。
保存值
1 | package main |
空接口的应用
空接口作为函数的参数
1 | package main |
空接口作为map的value
1 | package main |
接口和类型之间的转换
go 语言中使用接口断言(type assertions) 将接口转换成另一外一个接口,也可以将接口转另外的类型。使用非常频繁。
类型断言
格式:
1 | t := i.(T) |
- i:代表接口变量
- T:代表转换的目标类型
- t:转换后的变量
如果i没有完全实现T接口的方法,这个语句会触发宕机,触发宕机不是很友好,因为有另一种保守写法。
1 | t, ok := i.(T) |
这种写法的好处就是如果发送接口未实现时,将会返回一个布尔值false
,即ok
的值,而且t的类型为0。正常实现时,ok
为true
。
接口转化为其他接口
例子:
1 | package main |
接口转化为类型
1 | package main |
执行结果:
1 | &{} |
判断接口中变量的类型
判断基本类型
1 | package main |
执行结果:
1 | string类型 |
判断接口类型
1 | package main |
执行结果:
1 | 刷脸 |