[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)
```
## 接口值
由于接口類型的值可以是任意一個實現了該接口的類型值,所以接口值除了需要記錄具體**值**之外,還需要記錄這個值屬于的**類型**。也就是說接口值由“類型”和“值”組成,鑒于這兩部分會根據存入值的不同而發生變化,我們稱之為接口的`動態類型`和`動態值`。

```
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`,如圖

可以使用`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`

**注意:**此時接口變量`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