interface類型可以定義一組方法,但是這些不需要實現。并且interface不能包含任何變量。到某個自定義類型(比如結構體Phone)要使用的時候,在根據具體情況把這些方法寫出來(實現)。
**基本語法**

小結說明
1)接口里的所有方法都沒有方法體,即接口的方法都是沒有實現的方法。接口體現了程序設計的多態和高內聚低偶合的思想。
2)Golang中的接口,不需要顯式的實現。只要一個變量,含有接口類型中的所有方法,那么這個變量就實現這個接口。因此,Golang中沒有implement這樣的關鍵字
**接口使用的應用場景**


示例:下面的sort函數傳入的變量實現下面的接口就可以使用sort這個方法


**注意事項和細節**
1)接口本身不能創建實例,但是可以指向一個實現了該接口的自定義類型的變量(實例)

2)接口中所有的方法都沒有方法體,即都是沒有實現的方法。
3)在Golang中,一個自定義類型需要將某個接口的所有方法都實現,我們說這個自定義類型實現了該接口。
4)一個自定義類型只有實現了某個接口,才能將該自定義類型的實例(變量)賦給接口類型
5)只要是自定義數據類型,就可以實現接口,不僅僅是結構體類型。

6)一個自定義類型可以實現多個接口

7)Golang接口中不能有任何變量

8)一個接口(比如A接口)可以繼承多個別的接口(比如B,C接口),這時如果要實現A接口,也必須將B,C接口的方法也全部實現。

9)interface類型默認是一個指針(引用類型),如果沒有對interface初始化就使用,那么會輸出nil
10)空接口interface{}沒有任何方法,所以所有類型都實現了空接口,即我們可以把任何一個變量賦給空接口。





**接口編程的最佳實踐**
實現對Hero結構體切片的排序:sort.Sort(dataInterface)
```
package main
import (
"fmt"
"sort"
"math/rand"
)
//1.聲明 Hero 結構體
type Hero struct{
Name string
Age int
}
//2.聲明一個 Hero 結構體切片類型
type HeroSlice \[\]Hero
//3.實現 Interface 接口
func (hs HeroSlice) Len() int {
return len(hs)
}
//Less 方法就是決定你使用什么標準進行排序
//1. 按 Hero 的年齡從小到大排序!!
func (hs HeroSlice) Less(i, j int) bool {
return hs\[i\].Age < hs\[j\].Age
//修改成對 Name 排序
//return hs\[i\].Name < hs\[j\].Name
}
func (hs HeroSlice) Swap(i, j int) {
//交換
// temp := hs\[i\]
// hs\[i\] = hs\[j\]
// hs\[j\] = temp
//下面的一句話等價于三句話
hs\[i\], hs\[j\] = hs\[j\], hs\[i\]
}
//1.聲明 Student 結構體
type Student struct{
Name string
Age int
Score float64
}
//將 Student 的切片,安 Score 從大到小排序!!
func main() {
//先定義一個數組/切片
var intSlice = \[\]int{0, -1, 10, 7, 90}
//要求對 intSlice 切片進行排序
//1. 冒泡排序...
//2. 也可以使用系統提供的方法
sort.Ints(intSlice)
fmt.Println(intSlice)
//請大家對結構體切片進行排序
//1. 冒泡排序...
//2. 也可以使用系統提供的方法
//測試看看我們是否可以對結構體切片進行排序
var heroes HeroSlice
for i := 0; i < 10 ; i++ {
hero := Hero{
Name : fmt.Sprintf("英雄|%d", rand.Intn(100)),
Age : rand.Intn(100),
}
//將 hero append 到 heroes 切片
heroes = append(heroes, hero)
}
//看看排序前的順序
for \_ , v := range heroes {
fmt.Println(v)
}
//調用 sort.Sort
sort.Sort(heroes)
fmt.Println("-----------排序后\------------") //看看排序后的順序
for _ , v := range heroes {
fmt.Println(v)
}
i := 10
j := 20
i, j = j, i
fmt.Println("i=", i, "j=", j) // i=20 j = 10
}
```
## 實現接口 vs 繼承
大家聽到現在,可能會對實現接口和繼承比較迷茫了, 這個問題,那么他們究竟有什么區別呢



對上面代碼的小結
1) 當 A 結構體繼承了 B 結構體,那么 A 結構就自動的繼承了 B 結構體的字段和方法,并且可以直接使用
2) 當 A 結構體需要擴展功能,同時不希望去破壞繼承關系,則可以去實現某個接口即可,因此我們可以認為:實現接口是對繼承機制的補充.

(1) 接口和繼承解決的解決的問題不同
繼承的價值主要在于:解決代碼的復用性和可維護性。
(2) 接口的價值主要在于:設計,設計好各種規范(方法),讓其它自定義類型去實現這些方法。
(3) 接口比繼承更加靈活 Person Student BirdAble LittleMonkey
接口比繼承更加靈活,繼承是滿足 is - a 的關系,而接口只需滿足 like - a 的關系。
(4)接口在一定程度上實現代碼解耦
## **面向對象編程-多態**
變量(實例)具有多種形態。面向對象的第三大特征,在 Go 語言,多態特征是通過接口實現的。可以按照統一的接口來調用不同的實現。這時接口變量就呈現不同的形態。(在面向對象的理論 中,多態性的一般定義為:同一個操作作用于不同的類的實例,將產生不同的執行結果。也即不同類的對象收到相同的消息時,將得到不同的結果。)
在前面的 Usb 接口案例,Usb usb ,既可以接收手機變量,又可以接收相機變量,就體現了 Usb 接口 多態特性。[點明]
### **接口體現多態的兩種形式**
(1)多態參數
在前面的 Usb 接口案例,Usb usb ,即可以接收手機變量,又可以接收相機變量,就體現了 Usb 接 口 多態。
```
package main import ( "fmt" )//聲明/定義一個接口
type Usb interface { //聲明了兩個沒有實現的方法
Start()
Stop()
}
type Phone struct {
}
//讓 Phone 實現 Usb 接口的方法
func (p Phone) Start() {
fmt.Println("手機開始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手機停止工作。。。")
}
type Camera struct {
}
//讓 Camera 實現 Usb 接口的方法
func (c Camera) Start() {
fmt.Println("相機開始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相機停止工作。。。")
}
//計算機
type Computer struct {
}
//編寫一個方法 Working 方法,接收一個 Usb 接口類型變量
//只要是實現了 Usb 接口 (所謂實現 Usb 接口,就是指實現了 Usb 接口聲明所有方法)
func (c Computer) Working(usb Usb) { //usb 變量會根據傳入的實參,來判斷到底是 Phone,還是 Camera
//通過 usb 接口變量來調用 Start 和 Stop 方法
usb.Start()
usb.Stop()
}
func main() {
//測試
//先創建結構體變量
computer := Computer{}
phone := Phone{}
camera := Camera{}
//關鍵點
computer.Working(phone)
computer.Working(camera) //
}
```
(2)多態數組(數組中正常只能放一種數據類型我們用接口提現多態)
給 Usb 數組中,存放 Phone 結構體 和 Camera 結構體變量
```
package main
import (
"fmt"
)
//聲明/定義一個接口
type Usb interface {
//聲明了兩個沒有實現的方法
Start()
Stop()
}
type Phone struct {
name string
}
//讓 Phone 實現 Usb 接口的方法
func (p Phone) Start() {
fmt.Println("手機開始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手機停止工作。。。")
}
type Camera struct {
name string
}
//讓 Camera 實現 Usb 接口的方法
func (c Camera) Start() {
fmt.Println("相機開始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相機停止工作。。。")
}
func main() {
//定義一個 Usb 接口數組,可以存放 Phone 和 Camera 的結構體變量
//這里就體現出多態數組
var usbArr \[3\]Usb
usbArr\[0\] = Phone{"vivo"}
usbArr\[1\] = Phone{"小米"}
usbArr\[2\] = Camera{"尼康"}
fmt.Println(usbArr)
}
```
## **類型斷言**

在進行類型斷言時,如果類型不匹配,就會報 panic, 因此進行類型斷言時,要確保原來的空接口指向的就是斷言的類型.
如何在進行斷言時,帶上檢測機制,如果成功就 ok,否則也不要報 panic

### **類型斷言的最佳實踐 1**
給 Phone 結構體增加一個特有的方法 call(), 當 Usb 接口接收的是 Phone 變量時,還需要調用 call方法, 走代碼:
```
package main
import (
"fmt"
)
//聲明/定義一個接口
type Usb interface {
//聲明了兩個沒有實現的方法
Start()
Stop()
}
type Phone struct {
name string
}
//讓 Phone 實現 Usb 接口的方法
func (p Phone) Start() {
fmt.Println("手機開始工作。。。")
}
func (p Phone) Stop() {
fmt.Println("手機停止工作。。。")
}
func (p Phone) Call() {
fmt.Println("手機 在打電話..")
}
type Camera struct {
name string
}
//讓 Camera 實現 Usb 接口的方法
func (c Camera) Start() {
fmt.Println("相機開始工作。。。")
}
func (c Camera) Stop() {
fmt.Println("相機停止工作。。。")
}
type Computer struct {
}
func (computer Computer) Working(usb Usb) {
usb.Start()
//如果 usb 是指向 Phone 結構體變量,則還需要調用 Call 方法
//類型斷言..\[注意體會!!!\]
if phone, ok := usb.(Phone); ok {
phone.Call()
}
usb.Stop()
}
func main() {
//定義一個 Usb 接口數組,可以存放 Phone 和 Camera 的結構體變量
//這里就體現出多態數組
var usbArr \[3\]Usb
usbArr\[0\] = Phone{"vivo"}
usbArr\[1\] = Phone{"小米"}
usbArr\[2\] = Camera{"尼康"}
//遍歷 usbArr
//Phone 還有一個特有的方法 call(),請遍歷 Usb 數組,如果是 Phone 變量,
//除了調用 Usb 接口聲明的方法外,還需要調用 Phone 特有方法 call. =》類型斷言
var computer Computer
for \_, v := range usbArr{
computer.Working(v)
fmt.Println()
}
//fmt.Println(usbArr)
}
```
### **類型斷言的最佳實踐 2**
寫一函數,循環判斷傳入參數的類型:
注意以下程序中typeJudge中用...代表不確定的個數的實參0-多個 interface{}空的接口代表著任意類型因為空接口 (因為所有類型都實現了空接口)

