這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
Golang面向API編程-interface(介面)
作者:尹正傑
著作權聲明:原創作品,謝絕轉載!否則將追究法律責任。
Golang並不是一種典型的物件導向編程(Object Oriented Programming,OOP,物件導向程式設計)語言。它在文法上不支援類和繼承的概念。沒有繼承是否就無法擁有多態行為了呢?答案是否定的,我們知道 Golang 中沒有 class 的概念,而是通過 interface 類型轉換支援在動態類型語言中常見的“鴨子類型”達到運行時多態的效果。
一.什麼是interface
簡單的說,interface是一組method的組合,我們通過interface來定義對象的一組行為。換句話說,一個 interface 類型定義了一個“方法集合”作為其介面。 interface類型的變數可以儲存含有屬於這個interface類型的任何類型的值,這時我們就說這個類型實現了這個介面。未被初始化的interface類型變數的零值為空白(nil)。
二.interface類型和值
介面類型實際上是一組method(方法)簽名的清單。interface 類型定義了一組方法,如果某個對象實現了某個介面的所有方法,則此對象就實現了此介面。介面也是一種資料類型。如果你聲明了一個介面變數,這個變數能夠儲存任何實現該介面的物件類型。最後,任意的類型都實現了空interface(我們這樣定義:interface{}),也就是包含 0 個method的interface。所以我喜歡給它起一個綽號叫“大胃王”。
定義一個interface以及調用方式如下:
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "fmt"12 "strings"13 )14 15 type Human struct {16 Name string //定義姓名17 string //內建的匿名欄位,我們用它來定義家庭住址。18 phone int //電話號碼19 }20 21 type Student struct {22 Human //匿名欄位,其類型就是我們上面自訂的類型。23 Classroom string //教室名稱24 Score float64 //考試成績25 }26 27 type Teacher struct {28 Human //匿名欄位29 Position string //職位資訊30 salary float64 //薪資情況31 }32 33 34 func (h Human) SayHi() { //定義結構體“Human”自我介紹的方法。35 fmt.Printf("Hello, my name is 【%s】. My phone number is【%d】.My home address is【%s】\n", h.Name,36 h.phone,h.string) //格式化輸出是可以換行的喲。37 }38 39 40 func (h Human) Sing(Name string) { //定義結構體“Human”唱歌的方法41 fmt.Printf("【%s】:我有一隻小毛驢我從來也不騎,有一天我心血來潮騎它去趕集.....\n", Name)42 }43 44 45 func (t Teacher) SayHi() { //定義結構體“Teacher”自我介紹的方法。46 fmt.Printf("Anyway, I'm your 【%s】 teacher, you can call me 【%s】,My salary is...【%f】\n", t.Position,47 t.Human.Name,t.salary)48 }49 50 51 type Superman interface { //定義一個介面52 SayHi() //這個介面包含“SayHi() ”方法。53 Sing(Name string) //該介面也包含“Sing(Name string)”方法。54 }55 56 func main() {57 yzj := Student{Human{"尹正傑", "北京", 7474741}, "三年級一班", 95} //初始化我們定義的結構體。我們也可以將這個過程叫做執行個體化。而將“yzj”叫做執行個體。58 hsy := Student{Human{"韓森雨", "北京", 2424241}, "一年級五班", 100}59 bingan := Teacher{Human{"餅乾", "北京", 6464641}, "Golang", 30000}60 61 62 var yinzhengjie Superman //聲明一個變數,其類型為我們定義的介面。63 64 yinzhengjie = yzj //注意了,這是接受了我們定義第一個類型。65 yinzhengjie.SayHi() //並且可以調用這個類型下的“SayHi()”方法已經“Sing("高音唱")”方法。66 yinzhengjie.Sing("高音唱")67 68 69 fmt.Println("\n",strings.Repeat("*",30),"我是分割線",strings.Repeat("*",30),"\n")70 71 yin := make([]Superman,3) //處理上面的那種最普遍的玩法,當然也可以用make方法將其定義成切片的方式。72 yin[0], yin[1], yin[2] = hsy, bingan, yzj //用下標來區分不同的實力。這個時候我們可以發現Superman類型可以接受“Student”和“Teacher”類型的資料,儘管這是兩個不同的結構體,但是照樣可以通過一個介面來調用,因此我叫它“大胃王”。73 74 for _, value := range yin{ //然後我們就可以同時調用3個方法啦!75 value.SayHi()76 }77 }78 79 80 81 #以上代碼執行結果如下:82 Hello, my name is 【尹正傑】. My phone number is【7474741】.My home address is【北京】83 【高音唱】:我有一隻小毛驢我從來也不騎,有一天我心血來潮騎它去趕集.....84 85 ****************************** 我是分割線 ****************************** 86 87 Hello, my name is 【韓森雨】. My phone number is【2424241】.My home address is【北京】88 Anyway, I'm your 【Golang】 teacher, you can call me 【餅乾】,My salary is...【30000.000000】89 Hello, my name is 【尹正傑】. My phone number is【7474741】.My home address is【北京】
通過上面的代碼我們可以知道,interface 可以被任意的對象實現。同理,一個對象可以實現任意多個interface。你會發現interface 就是一組抽象方法的集合,它必須由其他非interface類型實現,而不能自我實現, go 通過 interface 實現了duck-typing:即"當看到一隻鳥走起來像鴨子、遊泳起來像鴨子、叫起來也像鴨子,那麼這隻鳥就可以被稱為鴨子"。
三.空interface
空interface(interface{})不包含任何的 method,正因為如此,所有的類型都實現了空interface。空 interface 對於描述起不到任何的作用(因為它不包含任何的 method),但是空interface 在我們需要儲存任意類型的數值的時候相當有用,因為它可以儲存任意類型的數值。一個函數把interface{}作為參數,那麼他可以接受任意類型的值作為參數,如果一個函數返回interface{},那麼也就可以返回任意類型的值。是不是瞬間就覺得interface很神奇!
1 /* 2 #!/usr/bin/env gorun 3 @author :yinzhengjie 4 Blog:http://www.cnblogs.com/yinzhengjie/tag/GO%E8%AF%AD%E8%A8%80%E7%9A%84%E8%BF%9B%E9%98%B6%E4%B9%8B%E8%B7%AF/ 5 EMAIL:y1053419035@qq.com 6 */ 7 8 package main 9 10 import (11 "fmt"12 "reflect"13 )14 15 func Myecho() {16 fmt.Println("我有一隻小毛驢我從來也不騎,有一天我心血來潮騎它去趕集.....")17 }18 19 func main() {20 Name := "尹正傑" //我這裡定義了三種不同的類型,即字串,整數,位元組數等等。21 Age := 1822 Language := []byte("Golang")23 fmt.Println(reflect.TypeOf(Name),reflect.TypeOf(Age),reflect.TypeOf(Language))24 var yinzhengjie interface{} //定義一個空的interface,由於每種資料類型都實現了空interface。因此我們利用這個特性可以接受任意類型的資料。25 yinzhengjie = Name 26 fmt.Printf("變數的值是:\033[31;1m【%v】\033[0m,其類型是:\033[31;1m【%v】\033[0m\n",yinzhengjie,reflect.TypeOf(yinzhengjie))27 yinzhengjie = Age28 fmt.Printf("變數的值是:\033[31;1m【%v】\033[0m,其類型是:\033[31;1m【%v】\033[0m\n",yinzhengjie,reflect.TypeOf(yinzhengjie))29 yinzhengjie = Language30 fmt.Printf("變數的值是:\033[31;1m【%v】\033[0m,其類型是:\033[31;1m【%v】\033[0m\n",yinzhengjie,reflect.TypeOf(yinzhengjie))31 }32 33 34 35 #以上代碼執行結果如下:36 string int []uint837 變數的值是:【尹正傑】,其類型是:【string】38 變數的值是:【18】,其類型是:【int】39 變數的值是:【[71 111 108 97 110 103]】,其類型是:【[]uint8】
四.interface 函數參數
interface 的變數可以持有任意實現該 interface 類型的對象,這給我們編寫函數(包括method)提供了一些額外的思考,我們是不是可以通過定義 interface 參數,讓函數接受各種類型的參數。舉個例子:fmt.Println 是我們常用的一個函數,但是你是否注意到它可以接受任意類型的資料。開啟fmt的源碼檔案,你會看到這樣一個定義: