Go語言學(xué)習(xí)之接口類型(interface)詳解
接口
接口是用來定義行為的類型,定義的行為不由接口直接實現(xiàn),而由通過方法由定義的類型實現(xiàn)
Golang中,接口是一組方法的簽名,是語言中一個重要的組成部分,其目的是通過引入一個中間層與具體的實現(xiàn)進行分離,達到解耦合的作用,同時隱藏底層實現(xiàn),減少關(guān)注點
Golang不同于Java,通過隱式實現(xiàn)聲明的接口,即只要實現(xiàn)了接口聲明中的方法,就是實現(xiàn)了接口,
接口的定義需要使用interface關(guān)鍵字,且在接口中只能定義方法簽名,不能包含成員變量
基于官方的io包進行分析:
type Reader interface { Read(p []byte) (n int, err error) }
上面是io包中聲明的Reader接口,如果一個類型需要實現(xiàn)Reader接口,那么就僅需要實現(xiàn)Read(p []byte) (n int, err error)
方法,如LimitedReader就實現(xiàn)了Reader接口:
type LimitedReader struct { R Reader N int64 } func(l *LimitedReader) Read(p []byte) (n int, err error) { if ;.N <= 0 { return 0, EOF } if int64(len(p)) > l.N { p = p[o:l.N] } n, err := l.R.Read(p) l.N -= int64(n) return }
Golang只會在參數(shù)傳遞、返回參數(shù)和變量賦值時對類型是否實現(xiàn)了某個接口進行檢查,接口在定義方法時對實現(xiàn)的接受者做限制,所以會有兩種方式實現(xiàn)接口:結(jié)構(gòu)體實現(xiàn)和指針實現(xiàn)。
但這兩種實現(xiàn)方式不可以同時存在,Go語言的編譯器會在結(jié)構(gòu)體類型和指針類型都實現(xiàn)同一個方法時報錯“method redeclared”
type Cat struct {} type Duck interface {} func (c Cat) Quack {} // 結(jié)構(gòu)體實現(xiàn) func (c *Cat) Quack {} // 指針實現(xiàn) var d Duck = Cat{} // 結(jié)構(gòu)體初始化 var d Duck = &Cat{} // 指針初始化
注意:指針實現(xiàn)接口,結(jié)構(gòu)體初始化變量是無法通過編譯的;而結(jié)構(gòu)體實現(xiàn)接口,指針初始化變量可以
(Golang在傳遞參數(shù)是值傳遞的,指針初始化變量時,指針可以隱式地獲取到指向的結(jié)構(gòu)體:c.i可以理解成(*c).i)
詳細理解就是在Golang中,初始化變量后進行方法調(diào)用時會發(fā)生`值拷貝`:
1.對于初始化的指針來說,意味著拷貝的新指針仍然與原指針一樣,指向一個相同且唯一的結(jié)構(gòu)體,所以編譯器可以隱式通過對變量的解引用(dereference)獲取到指針的結(jié)構(gòu)體
2.而對于結(jié)構(gòu)體而言,這是拷貝生成了新的結(jié)構(gòu)體,但方法的參數(shù)是指針,編譯器既不可能創(chuàng)建一個新的指針,即使創(chuàng)建也無法指向最初調(diào)用該方法的結(jié)構(gòu)體
具體的例子如Goinaction的代碼示例:
listing36.go
package main import ( "fmt" ) type notifier interface { notify() } type user struct { name string email string } func (u *user) notify() { fmt.Printf("Sending user email to %s<%s>)\n", u.name, u.email) } func main() { u := user{"Bill", "bill@email.com"} sendNotification(u) } func sendNotification(n notifier) { n.notify() }
仔細查看代碼就會發(fā)現(xiàn)u是一個結(jié)構(gòu)體類型,而notify方法是使用指針接受者實現(xiàn)的,上述代碼自然就無法編譯通過
數(shù)據(jù)結(jié)構(gòu)
Golang根據(jù)接口類型是否包含一組方法將接口類型分成兩類:
1.使用runtime.iface結(jié)構(gòu)體表示包含方法的接口
type iface struct { tab *itab // runtime.itab類型結(jié)構(gòu)體,接口類型的核心組成部分 data unsafe.Pointer // 指向原始數(shù)據(jù)的指針 }
2.使用runtime.eface結(jié)構(gòu)體表示不包含任何方法的interface{}類型
type eface struct { _type *_type // 指向類型的指針 data unsafe.Pointer // 指向底層數(shù)據(jù)的指針 }
接口類型不是任意類型
注意:interface{}類型不是任意類型,如果將類型轉(zhuǎn)換成interface{}類型,變量在運行期間的類型也會發(fā)生變化,獲取變量類型時會得到interface{}
package main type Test struct {} func NilOrNot(v interface{}) bool { return v == nil } func main() { var s *Test fmt.Println(s == nil) fmt.Println(NilorNot(s)) }
運行上述代碼時,第一行打印true,第二行會打印false。
出現(xiàn)上述現(xiàn)象的原因就是在調(diào)用NilOrNot函數(shù)時發(fā)生了隱式的類型轉(zhuǎn)換:*Test類型轉(zhuǎn)換成interface{},除了這種傳參的情況,在變量賦值也是如此
動態(tài)派發(fā)(Dynamic dispatch)是在運行期間選擇具體多態(tài)操作(方法或函數(shù))執(zhí)行的過程,接口的引入使得Golang具備了動態(tài)派發(fā)的特性,即在調(diào)用接口類型的方法時,如果編譯期間不能確認接口的類型,則會在運行期間決定具體調(diào)用該方法的具體實現(xiàn)
動態(tài)派發(fā)在結(jié)構(gòu)體上的表現(xiàn)非常差,應(yīng)當盡量避免使用結(jié)構(gòu)體類型實現(xiàn)接口
到此這篇關(guān)于Go語言學(xué)習(xí)之接口類型(interface)詳解的文章就介紹到這了,更多相關(guān)Go語言接口類型內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Golang算法問題之整數(shù)拆分實現(xiàn)方法分析
這篇文章主要介紹了Golang算法問題之整數(shù)拆分實現(xiàn)方法,結(jié)合實例形式分析了Go語言數(shù)值運算與數(shù)組遍歷相關(guān)操作技巧,需要的朋友可以參考下2017-02-02Golang HTTP請求Json響應(yīng)解析方法以及解讀失敗的原因
這篇文章主要介紹了Golang HTTP請求Json響應(yīng)解析方法以及解讀失敗的原因,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-03-03