Go編程基礎——接口interface

接口interface
1、接 囗 是 一 個 或 多 個 方法 籤 名 的 集 合
2、 只 要 某 個 類 型 擁 有 該 接 囗 的 所 有 方 法 籤 名 , 即 算 實 現 該接口, 無 需 顯式
聲 明 實 現 了 哪 個 接 囗 , 這 稱 爲 Structural Typing
3、接 囗 只 有 方法 聲 明 , 沒 有 實 現 , 沒 有數據字段
4、接 囗 可 以 匿 名 嵌 入 其 它 接 囗 , 或 嵌 入結構中
5、將 對 象 賦 值 給 接 口 時 , 會 發 生 拷 貝 , 而 接 口 內 部 存 儲 的 是 指 向 這 個
復 制 品 的 指 針 , 既無法修 改 復 制 品 的 狀 態, 也 無法獲取指針(換句話說就是值拷貝)
6、只 有 當 接 囗 存 儲 的 類 型 和 對 象 都 爲 nil 時 , 接 囗 才 等 於nil
7、接 囗 調 用 不 會 做 receiver自動轉換
8、接 囗 同 樣 支 持 匿 名 字 段 方 法
9、接 囗 也 可 實 現 類 似 OOP 中 的 多 態
10、空 接 囗 可 以 作 爲 可 類 型 數 據 的 容 器
首先來一個案例包括定義一個接口,一個結構,使用這個結構實現前邊定義接口中聲明的方法。

type USB interface {
   Name() string
   Connect()
}
type TelephoneConnector struct{
   name string
   brand string
}
func (tc TelephoneConnector) Name() string{
   return tc.name
}
func (tc TelephoneConnector) Connect() {
   fmt.Println("Connect:",tc.name)
}

func disconnect(usb USB){
   fmt.Println("disconnect is success!")
}
func main() {
   var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
   a.Connect()
   disconnect(a)
}

運行結果

Connect: TelephoneConnector
disconnect is success!

上邊的程序首先是定義了一個接口USB,其中聲明瞭兩個方法的簽名Name() string和Connect,接着定義了一個結構Telephone,在結構中定義了兩個字段name和brand,這樣Telephone是一個類型,也就有了main方法中定義一個變量a爲USB,這裏就需要初始化變量的值(因爲定義的兩個字段都是string,不初始化值不容易看出程序的運行過程。)
按照USB接口中定義方法簽名以及對返回值的聲明來構建兩個方法。第一個參數是receiver的類型(結構類型),其實這一步也就在告訴編譯器,這個接收器(對應的結構)實現對應的接口。
主函數中a變量可以使用

a:=Telephone{"TelephoneConnector",
   "MeiZu"}
var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}

兩種方式,第一種不容看出它是否實現了USB,所以就有了接下來disconnect(usb USB)方法穿入a,能夠成功的實現也就是說明了a實現了USB接口。其實變量a還可以使用

var a TelephoneConnector
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}

畢竟TelephoneConnector是USB接口的具體實現,直接定義爲TelephoneConnector類型也是可以的。
嵌入接口(在Java中的接口的多繼承的關係——爲了靈活性)
嵌入接口與嵌入接口一樣的實現方式,深刻地用到Go語言中重要的概念——組合
這裏要注意的組合不同嵌入接口的時候,外層接口和嵌入接口定義的方法簽名一致的話,外層接口的同名方法簽名會報錯

duplicate method 'Connect' less... (Ctrl+F1)
Reports names redeclared in this block.
type USB interface {
   Connector
   Name() string
}
type Connector interface {
   Connect()
}

type TelephoneConnector struct{
   name string
   brand string
}
func (tc TelephoneConnector) Name() string{
   return tc.name
}
func (tc TelephoneConnector) Connect() {
   fmt.Println("Connect:",tc.name)
}

func disconnect(usb USB){
   fmt.Println("disconnect is success!")
}
func main() {
   var a USB
   a=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
   a.Connect()
   disconnect(a)
}

運行結果

Connect: TelephoneConnector
disconnect is success!

成功運行,這裏是使用Connector接口嵌入在USB接口中。
其實這裏的問題是如何知道調用disconnect()方法是Telephone,畢竟實現USB接口可能不止是Telephone這個結構,同時這裏的usb類型是無法調用Telephone中的name字段的。這就需要改裝disconnect方法來實現我們的猜想。
利用ok parse的表達方式來實現是一個常用的技巧。

func disconnect(usb USB){
   if tc,ok:=usb.(TelephoneConnector);ok{
      fmt.Println("func in disconnect:",tc.name)
      return
   }
   fmt.Println("unknown type!")
}

運行結果

Connect: TelephoneConnector
func in disconnect: TelephoneConnector

這裏還有兩點要注意的,用一個接口判斷一個結構是否屬於該接口的方法類似usb.(TelephoneConnector);if判斷之後記得要添加上return不然程序會繼續執行之後的程序。
最廣泛的接口是空接口,interface{}
改造上邊的disconnect()方法,這樣實現空接口也是一樣可以的,並不影響之前的運行結果。

func disconnect(usb interface{}){
   if tc,ok:=usb.(TelephoneConnector);ok{
      fmt.Println("func in disconnect:",tc.name)
      return
   }
   fmt.Println("unknown type!")
}
func disconnect(usb interface{}){
   switch tc:=usb.(type) {
   case TelephoneConnector:
      fmt.Println("func in disconnect:",tc.name)
   default:
      fmt.Println("unknown type!")

   }
}

接口之間實現轉化
強制類型轉換,和類型的大小有關。

tel:=TelephoneConnector{"TelephoneConnector",
   "MeiZu"}
var a Connector
a=Connector(tel)
a.Connect()
disconnect(a)

這時候a沒有name屬性。

類 型 斷 言
通 過 類 型 斷 言 的 ok pattern 可 以 判 斷 接 囗 中 的 數 據 類 型
使用type switch 則 可 針 對 空 接 囗 進 行 比 較 全 面 的 類 型 判 斷
接 囗 轉 換
- 可 以 將 擁 有 超 集 的 接 口轉換爲子集的接 囗

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章