[TOC]
# 接口
谨记:接口(interface)是一种类型,一种抽象的类型
## 接口类型
接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。
### 接口的定义
每个接口类型由任意个方法签名组成,格式如下:
```
type interface_name interface{
function_name1( [param] ) [return_values]
function_name2( [param] ) [return_values]
…
}
```
其中:
* type关键字表示要自定义类型;
* interface_name是自定义的接口名;interface表示接口类型;由大括号包裹的部分定义了要被实现方法,一个接口中可以同时存在一个或多个方法。
* function_name是方法名;params是方法所需的参数,return_values是方法的返回值。params和return_values可以省略,也可以存在一个或多个。
* 在为接口命名时,一般会在单词后面加上er后缀
eg:
```
type personer interface{
…
}
```
## 接口的实现
接口就是规定了一个**需要实现的方法列表**,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。
```
type player interface{
play()
}
type jinglecat struct{}
func (j *jinglecat) play(){
fmt.Println("播放了3集~")
}
```
因为`player`接口只包含一个`play`方法,所以只需要给`jinglecat`结构体添加一个`play`方法就可以满足`player`接口的要求,此时就可以说是实现了接口
### 为什么要使用接口
```
type jinglecat struct{}
func (j *jinglecat) play(){
fmt.Println("小叮当播放了3集~")
}
type naruto struct{}
func (n *naruto) play(){
fmt.Println("火影播放了3集~")
}
func main(){
cat := new(jinglecat)
cat.play() //小叮当播放了3集~
nar := new(naruto)
nar.play() //火影播放了3集~
}
```
还有很多的动画片也播放3集了那就需要写很多的以上重复代码,那么,定义一个接口
```
type player interface{
play()
}
func comicPlay(p player) {
p.play()
}
func main() {
var cater player
cater = new(jinglecat)
comicPlay(cater) //小叮当播放了3集~
cater = &naruto{}
comicPlay(cater)//火影播放了3集~
}
```
就相当于面向接口编程,根本不关心某个类型具体的play怎么实现,我们关心play能不能正常实现
## 值接收者和指针接收者
### 值接收者实现接口
```
type player interface{
play()
}
type jinglecat struct{}
func (j jinglecat) play(){
fmt.Println("小叮当播放了3集~")
}
type naruto struct{}
func (n *naruto) play(){
fmt.Println("火影播放了3集~")
}
func main(){
cat := jinglecat{}
cat.play() //小叮当播放了3集~
}
```
### 指针接收者实现接口
```
type player interface{
play()
}
type naruto struct{}
func (n *naruto) play(){
fmt.Println("火影播放了3集~")
}
func main(){
nar := new(naruto) //或者&naruto{}
nar.play() //火影播放了3集~
}
```
区别:
* 在接口背景下,对于值接收者实现的接口,无论使用值类型还是指针类型都没有问题,但是当是指针类型时候只能是指针。
* 在非接口情况下,值类型和指针类型的方法,可以互相调用
## 类型与接口的关系
### 一个类型实现多个接口
```
type mover interface {
move()
}
type sayer interface {
say()
}
type cat struct {
Name string
}
func (c *cat) move() {
fmt.Printf("%s 咔咔咔跑 \n", c.Name)
}
func (c *cat) say() {
fmt.Printf("%s wawa叫 \n", c.Name)
}
func main() {
var d1 mover
x := &cat{
Name: "叫我小叮当啊",
}
d1 = x
d1.move() //叫我小叮当啊 咔咔咔跑
var d2 sayer
d2 = x
d2.say() //叫我小叮当啊 wawa叫
}
```
### 多种类型实现同一接口
```
type mover interface {
move()
}
type cat struct {
name string
}
func (c *cat) move() {
fmt.Printf("%s 咔咔跑 \n", c.name)
}
type car struct {
name string
}
func (c *car) move() {
fmt.Printf("%s 呜呜跑~ \n", c.name)
}
func main() {
var moveType mover
dog := &cat{
name: "叫我小叮当啊",
}
moveType = dog
moveType.move()//叫我小叮当啊 咔咔跑
car := &car{
name: "火车",
}
moveType = car
moveType.move()//火车 呜呜跑~
}
```
### 嵌入结构体实现接口
```
type personer interface {
eat()
drink()
}
type man struct {
sex string
}
func (m *man) eat() {
fmt.Printf("%s 会吃肉 \n", m.sex)
}
type women struct {
name string
man
}
func (w *women) drink() {
fmt.Printf("名字是%s 的 %s 还能喝酒\n", w.name, w.sex)
}
func main() {
var person personer
women := &women{
name: "露娜",
man: man{
sex: "女人",
},
}
person = women
person.eat()
person.drink()
}
```
### 接口互相嵌套
```
type personer interface {
drinker
eater
}
type drinker interface {
drink()
}
type eater interface {
eat()
}
type wan struct {
name string
}
func (w *wan) eat() {
fmt.Printf("%s 贼能吃\n", w.name)
}
func (w *wan) drink() {
fmt.Printf("%s 还贼能喝\n", w.name)
}
func main() {
var newperson personer
wan := &wan{
name: "叫我小叮当啊",
}
newperson = wan
newperson.eat()
newperson.drink()
}
```
## 空接口
### 空接口的定义
空接口是指没有定义任何方法的接口类型。因此任何类型都可以视为实现了空接口。也正是因为空接口类型的这个特性,空接口类型的变量可以存储任意类型的值。
```
// Any 不包含任何方法的空接口类型
type Any interface{}
func main() {
var x Any
x = "你好" // 字符串型
fmt.Printf("type:%T value:%v\n", x, x)
x = 100 // int型
fmt.Printf("type:%T value:%v\n", x, x)
x = true // 布尔型
fmt.Printf("type:%T value:%v\n", x, x)
}
```
通常我们在使用空接口类型时不必使用`type`关键字声明,可以像下面的代码一样直接使用`interface{}`。
```
var x interface{} // 声明一个空接口类型变量x
```
### 空接口的应用
#### 空接口作为函数的参数
使用空接口实现可以接收任意类型的函数参数。
```
// 空接口作为函数参数
func show(a interface{}) {
fmt.Printf("type:%T value:%v\n", a, a)
}
```
#### 空接口作为map的值
使用空接口实现可以保存任意值的字典。
```
var studentInfo = make(map[string]interface{})
studentInfo["name"] = "叫我小叮当啊"
studentInfo["age"] = 18
studentInfo["gener"] = "男"
fmt.Println(studentInfo)
```
## 接口值
由于接口类型的值可以是任意一个实现了该接口的类型值,所以接口值除了需要记录具体**值**之外,还需要记录这个值属于的**类型**。也就是说接口值由“类型”和“值”组成,鉴于这两部分会根据存入值的不同而发生变化,我们称之为接口的`动态类型`和`动态值`。
![](https://img.kancloud.cn/73/19/73190886381a4c5257c6a84596df3393_454x278.png)
```
type Mover interface {
Move()
}
type Dog struct {
Name string
}
func (d *Dog) Move() {
fmt.Println("狗在跑~")
}
type Car struct {
Brand string
}
func (c *Car) Move() {
fmt.Println("汽车在跑~")
}
```
创建一个`Mover`接口类型的变量`m`。
```
var m Mover
```
此时,接口变量`m`是接口类型的零值,也就是它的类型和值部分都是`nil`,如图
![](https://img.kancloud.cn/92/53/9253a41b2b2cd12e53cd817ee0b6f331_420x277.png)
可以使用`m == nil`来判断此时的接口值是否为空。
```
fmt.Println(m == nil) // true
```
**注意:**我们不能对一个空接口值调用任何方法,否则会产生panic。
```
m.Move() // panic: runtime error: invalid memory address or nil pointer dereference
```
我们给接口变量`m`赋值为一个`*Car`类型的值。
```
m = new(Car)
```
接口值的动态类型为`*Car`,动态值为`nil`
![](https://img.kancloud.cn/53/5e/535e9cf5d4df54e0a5a381cb6f6b9bc9_845x304.png)
**注意:**此时接口变量`m`与`nil`并不相等,因为它只是动态值的部分为`nil`,而动态类型部分保存着对应值的类型。
**接口值是支持相互比较的,当且仅当接口值的动态类型和动态值都相等时才相等。有两种数据是无法比较的,它们是:Map和Slice**
## 类型断言
格式:
```
value, ok := x.(T)
```
其中:
* x:表示接口类型的变量
* T:表示断言`x`可能是的类型。
```
type Cube struct {
length float64
}
func (c *Cube) cubeVolume() float64 {
return math.Pow(c.length, 3)
}
type Cuboid struct {
length float64
width float64
height float64
}
func (c *Cuboid) CuboidVolume() float64 {
return c.height * c.length * c.width
}
type Cylinder struct {
diameter float64
height float64
}
func (c *Cylinder) CylinderVolume() float64 {
return math.Pi * math.Pow((c.diameter/2), 2) * c.height
}
func calcSize(material interface{}) float64 {
switch data := material.(type) {
case Cube:
return data.cubeVolume()
case Cuboid:
return data.CuboidVolume()
case Cylinder:
return data.CylinderVolume()
default:
return 0
}
}
func main() {
countSize := 0.0
var sizeSlice []interface{}
sizeSlice = append(sizeSlice, Cube{length: 12})
sizeSlice = append(sizeSlice, Cuboid{length: 12.0, width: 4.5, height: 3.0})
sizeSlice = append(sizeSlice, Cylinder{height: 3.0, diameter: 6.5})
for _, value := range sizeSlice {
countSize += calcSize(value)
}
fmt.Println(countSize)
}
```
- Go准备工作
- 依赖管理
- Go基础
- 1、变量和常量
- 2、基本数据类型
- 3、运算符
- 4、流程控制
- 5、数组
- 数组声明和初始化
- 遍历
- 数组是值类型
- 6、切片
- 定义
- slice其他内容
- 7、map
- 8、函数
- 函数基础
- 函数进阶
- 9、指针
- 10、结构体
- 类型别名和自定义类型
- 结构体
- 11、接口
- 12、反射
- 13、并发
- 14、网络编程
- 15、单元测试
- Go常用库/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go优化
- Go问题排查
- Go框架
- 基础知识点的思考
- 面试题
- 八股文
- 操作系统
- 整理一份资料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小点
- 树
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面试题
- 基础
- Map
- Chan
- GC
- GMP
- 并发
- 内存
- 算法
- docker