go語言可以開發(fā)接口。go語言中接口是一組方法的簽名,它是go語言中重要的組成部分,接口做的事情就好像是定義一個(gè)規(guī)范或者協(xié)議,各個(gè)實(shí)現(xiàn)方只要按照協(xié)議實(shí)現(xiàn)即可。go語言中使用interface關(guān)鍵字來定義接口,語法“type 接口類型名 interface{方法名1( 參數(shù)列表1 ) 返回值列表1 方法名2( 參數(shù)列表2 ) 返回值列表2…}”。
php入門到就業(yè)線上直播課:進(jìn)入學(xué)習(xí)
Apipost = Postman + Swagger + Mock + Jmeter 超好用的API調(diào)試工具:點(diǎn)擊使用
本教程操作環(huán)境:windows7系統(tǒng)、GO 1.18版本、Dell G3電腦。
接口對于我們來說應(yīng)該是一個(gè)比較熟悉的概念,在各種開發(fā)語言中運(yùn)用都非常的廣泛,對于像我們比較熟悉java的程序員來說對于接口就更加的親切,下面我們來看下在go語言中接口是怎么用,以及接口在日常開發(fā)中發(fā)揮的作用。
概念
go語言中接口是一組方法的簽名,它是go語言中重要的組成部分,接口做的事情就好像是定義一個(gè)規(guī)范或者協(xié)議,各個(gè)實(shí)現(xiàn)方只要按照協(xié)議實(shí)現(xiàn)即可。
接口是一種類型
接口類型是對其他類型行為的抽象和概括,不關(guān)心具體的實(shí)現(xiàn)細(xì)節(jié),這種抽象的方式可以讓我們的函數(shù)變的更加靈活。
接口定義
type 接口類型名 interface{ 方法名1( 參數(shù)列表1 ) 返回值列表1 方法名2( 參數(shù)列表2 ) 返回值列表2 … }
在go語言中我們使用interface關(guān)鍵字來定義接口。
接口實(shí)現(xiàn)條件
如果一個(gè)任意類型T的方法集為一個(gè)接口類型的方法集的超集,則我們說類型T實(shí)現(xiàn)了該接口。
接口的實(shí)現(xiàn)在go語言中是隱式的,也就說兩個(gè)類型之間的實(shí)現(xiàn)關(guān)系不需要在代碼中體現(xiàn)出來,G哦語言中也沒有類似java中implements的關(guān)鍵字,Go編譯器將自動(dòng)在需要的時(shí)候檢查兩個(gè)類型之間的實(shí)現(xiàn)關(guān)系。接口定義后,需要實(shí)現(xiàn)接口,調(diào)用方才能正確編譯通過并使用接口。
接口的實(shí)現(xiàn)需要遵循兩條規(guī)則才能讓接口可用:
1、接口的方法與實(shí)現(xiàn)接口的類型方法格式一致在類型中添加與接口簽名一致的方法就可以實(shí)現(xiàn)該方法。簽名包括方法中的名稱、參數(shù)列表、返回參數(shù)列表。也就是說,只要實(shí)現(xiàn)接口類型中的方法的名稱、參數(shù)列表、返回參數(shù)列表中的任意一項(xiàng)與接口要實(shí)現(xiàn)的方法不一致,那么接口的這個(gè)方法就不會(huì)被實(shí)現(xiàn)。
package main import "fmt" type Phone interface { Call() SendMessage() } type HuaWei struct { Name string Price float64 } func (h *HuaWei) Call() { fmt.Printf("%s:可以打電話",h.Name) } func (h *HuaWei) SendMessage() { fmt.Printf("%s:可以發(fā)送短信",h.Name) } func main() { h := new(HuaWei) h.Name="華為" var phone Phone phone = h phone.SendMessage() }
當(dāng)類型無法實(shí)現(xiàn)接口時(shí),編譯器會(huì)報(bào)錯(cuò):
a.函數(shù)名稱不一致導(dǎo)致的報(bào)錯(cuò)
b.實(shí)現(xiàn)函數(shù)的方法簽名不一致導(dǎo)致的報(bào)錯(cuò)
2、接口中所有方法均被實(shí)現(xiàn)當(dāng)一個(gè)接口中有多個(gè)方法時(shí),只有這些方法都被實(shí)現(xiàn)了,接口才能被正確編譯并使用
func (h *Xiaomi) Call() { fmt.Printf("%s:可以打電話",h.Name) } func main() { h := new(Xiaomi) h.Name="小米" var phone Phone phone = h phone.SendMessage() }
當(dāng) 小米 類型僅僅實(shí)現(xiàn)接口中的一個(gè)方法的時(shí)候,我們在使用的時(shí)候,編譯報(bào)錯(cuò)。
類型與接口的關(guān)系
一個(gè)類型實(shí)現(xiàn)多個(gè)接口
一個(gè)類型可以實(shí)現(xiàn)多個(gè)接口,而接口之間彼此獨(dú)立,不知道對方的實(shí)現(xiàn)。
例如,狗既可以動(dòng),可以叫
package main import "fmt" type Move interface { move() } type Say interface { say() } type Dog struct { Name string } func (d *Dog) move() { fmt.Printf("%s會(huì)動(dòng)n", d.Name) } func (d *Dog) say() { fmt.Printf("%s會(huì)叫汪汪汪n", d.Name) } func main() { var m Move var s Say d:=&Dog{ Name: "旺財(cái)", } m = d s=d m.move() s.say() }
多個(gè)類型實(shí)現(xiàn)同一個(gè)接口
Go語言中不同的類型還可以實(shí)現(xiàn)同一接口 首先我們定義一個(gè)Mover接口,它要求必須有一個(gè)move方法。
type Mover interface { move() }
例如狗可以動(dòng),汽車也可以動(dòng),可以使用如下代碼實(shí)現(xiàn)這個(gè)關(guān)系:
type dog struct { name string } type car struct { brand string } // dog類型實(shí)現(xiàn)Mover接口 func (d dog) move() { fmt.Printf("%s會(huì)跑n", d.name) } // car類型實(shí)現(xiàn)Mover接口 func (c car) move() { fmt.Printf("%s速度70邁n", c.brand) }
這個(gè)時(shí)候我們在代碼中就可以把狗和汽車當(dāng)成一個(gè)會(huì)動(dòng)的物體來處理了,不再需要關(guān)注它們具體是什么,只需要調(diào)用它們的move方法就可以了。
func main() { var x Mover var a = dog{name: "旺財(cái)"} var b = car{brand: "保時(shí)捷"} x = a x.move() x = b x.move() }
空接口
空接口:interface{},不包含任何方法,正因?yàn)槿绱?,任何類型都?shí)現(xiàn)了空接口,所以空接口可以存儲(chǔ)任意類型的數(shù)據(jù)。
fmt
包下的 Print
系列函數(shù),其參數(shù)大多是空接口類型,也可以說支持任意類型
func Print(a ...interface{}) (n int, err error) func Println(format string, a ...interface{}) (n int, err error) func Println(a ...interface{}) (n int, err error)
空接口作為map的值
// 空接口作為map值 var studentInfo = make(map[string]interface{}) studentInfo["name"] = "李白" studentInfo["age"] = 18 studentInfo["married"] = false fmt.Println(studentInfo)
類型推斷
空接口可以存儲(chǔ)任意類型的值,那我們?nèi)绾潍@取其存儲(chǔ)的具體數(shù)據(jù)呢?
接口值
一個(gè)接口的值(簡稱接口值)是由一個(gè)具體類型和具體類型的值兩部分組成的。
這兩部分分別稱為接口的動(dòng)態(tài)類型和動(dòng)態(tài)值。
想要判斷空接口中的值這個(gè)時(shí)候就可以使用類型斷言,其語法格式:
x.(T)
其中:
-
x:表示類型為interface{}的變量
-
T:表示斷言x可能是的類型。
該語法返回兩個(gè)參數(shù),第一個(gè)參數(shù)是x轉(zhuǎn)化為T類型后的變量,第二個(gè)值是一個(gè)布爾值,若為true則表示斷言成功,為false則表示斷言失敗。
func main() { var x interface{} x = "ms的go教程" v, ok := x.(string) if ok { fmt.Println(v) } else { fmt.Println("類型斷言失敗") } }
上面的示例中如果要斷言多次就需要寫多個(gè)if判斷,這個(gè)時(shí)候我們可以使用switch語句來實(shí)現(xiàn):
func justifyType(x interface{}) { switch v := x.(type) { case string: fmt.Printf("x is a string,value is %vn", v) case int: fmt.Printf("x is a int is %vn", v) case bool: fmt.Printf("x is a bool is %vn", v) default: fmt.Println("unsupport type!") } }
因?yàn)榭战涌诳梢源鎯?chǔ)任意類型值的特點(diǎn),所以空接口在Go語言中的使用十分廣泛。
總結(jié)
關(guān)于接口需要注意的是,只有當(dāng)有兩個(gè)或兩個(gè)以上的具體類型必須以相同的方式進(jìn)行處理時(shí)才需要定義接口。不要為了接口而寫接口,那樣只會(huì)增加不必要的抽象,導(dǎo)致不必要的運(yùn)行時(shí)損耗。
【