這篇文章給大家分享的是有關(guān)golang中interface接口怎么用的內(nèi)容。小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,一起跟隨小編過(guò)來(lái)看看吧。
為化德等地區(qū)用戶(hù)提供了全套網(wǎng)頁(yè)設(shè)計(jì)制作服務(wù),及化德網(wǎng)站建設(shè)行業(yè)解決方案。主營(yíng)業(yè)務(wù)為網(wǎng)站設(shè)計(jì)制作、成都網(wǎng)站制作、化德網(wǎng)站設(shè)計(jì),以傳統(tǒng)方式定制建設(shè)網(wǎng)站,并提供域名空間備案等一條龍服務(wù),秉承以專(zhuān)業(yè)、用心的態(tài)度為用戶(hù)提供真誠(chéng)的服務(wù)。我們深信只要達(dá)到每一位用戶(hù)的要求,就會(huì)得到認(rèn)可,從而選擇與我們長(zhǎng)期合作。這樣,我們也可以走得更遠(yuǎn)!一 接口介紹
如果說(shuō)gorountine和channel是支撐起Go語(yǔ)言的并發(fā)模型的基石,讓Go語(yǔ)言在如今集群化與多核化的時(shí)代成為一道亮麗的風(fēng)景,那么接口是Go語(yǔ)言整個(gè)類(lèi)型系列的基石,讓Go語(yǔ)言在基礎(chǔ)編程哲學(xué)的探索上達(dá)到前所未有的高度。Go語(yǔ)言在編程哲學(xué)上是變革派,而不是改良派。這不是因?yàn)镚o語(yǔ)言有g(shù)orountine和channel,而更重要的是因?yàn)镚o語(yǔ)言的類(lèi)型系統(tǒng),更是因?yàn)镚o語(yǔ)言的接口。Go語(yǔ)言的編程哲學(xué)因?yàn)橛薪涌诙呌谕昝?。C++,Java 使用"侵入式"接口,主要表現(xiàn)在實(shí)現(xiàn)類(lèi)需要明確聲明自己實(shí)現(xiàn)了某個(gè)接口。這種強(qiáng)制性的接口繼承方式是面向?qū)ο缶幊趟枷氚l(fā)展過(guò)程中一個(gè)遭受相當(dāng)多質(zhì)疑的特性。Go語(yǔ)言采用的是“非侵入式接口",Go語(yǔ)言的接口有其獨(dú)到之處:只要類(lèi)型T的公開(kāi)方法完全滿(mǎn)足接口I的要求,就可以把類(lèi)型T的對(duì)象用在需要接口I的地方,所謂類(lèi)型T的公開(kāi)方法完全滿(mǎn)足接口I的要求,也即是類(lèi)型T實(shí)現(xiàn)了接口I所規(guī)定的一組成員。這種做法的學(xué)名叫做Structural Typing,有人也把它看作是一種靜態(tài)的Duck Typing。
要這個(gè)值實(shí)現(xiàn)了接口的方法。
type Reader interface { Read(p []byte) (n int, err os.Error) } // Writer 是包裹了基礎(chǔ) Write 方法的接口。 type Writer interface { Write(p []byte) (n int, err os.Error) } var r io.Reader r = os.Stdin r = bufio.NewReader(r) r = new(bytes.Buffer)
有一個(gè)事情是一定要明確的,不論 r 保存了什么值,r 的類(lèi)型總是io.Reader
,Go 是靜態(tài)類(lèi)型,而 r 的靜態(tài)類(lèi)型是 io.Reader。接口類(lèi)型的一個(gè)極端重要的例子是空接口interface{},它表示空的方法集合,由于任何值都有零個(gè)或者多個(gè)方法,所以任何值都可以滿(mǎn)足它。也有人說(shuō) Go 的接口是動(dòng)態(tài)類(lèi)型的,不過(guò)這是一種誤解。 它們是靜態(tài)類(lèi)型的:接口類(lèi)型的變量總是有著相同的靜態(tài)類(lèi)型,這個(gè)值總是滿(mǎn)足空接口,只是存儲(chǔ)在接口變量中的值運(yùn)行時(shí)可能被改變。對(duì)于所有這些都必須嚴(yán)謹(jǐn)?shù)膶?duì)待,因?yàn)榉瓷浜徒涌诿芮邢嚓P(guān)。
二 接口類(lèi)型內(nèi)存布局
在類(lèi)型中有一個(gè)重要的類(lèi)別就是接口類(lèi)型,表達(dá)了固定的一個(gè)方法集合。一個(gè)接口變量可以存儲(chǔ)任意實(shí)際值(非接口),只要這個(gè)值實(shí)現(xiàn)了接口的方法。interface在內(nèi)存上實(shí)際由兩個(gè)成員組成,如下圖,tab指向虛表,data則指向?qū)嶋H引用的數(shù)據(jù)。虛表描繪了實(shí)際的類(lèi)型信息及該接口所需要的方法集。
type Stringer interface { String() string } type Binary uint64 func (i Binary) String() string { return strconv.FormatUint(i.Get(), 2) } func (i Binary) Get() uint64 { return uint64(i) } func main() { var b Binary = 32 s := Stringer(b) fmt.Print(s.String()) }
觀察itable的結(jié)構(gòu),首先是描述type信息的一些元數(shù)據(jù),然后是滿(mǎn)足Stringger接口的函數(shù)指針列表(注意,這里不是實(shí)際類(lèi)型Binary的函數(shù)指針集哦)。因此我們?nèi)绻ㄟ^(guò)接口進(jìn)行函數(shù)調(diào)用,實(shí)際的操作其實(shí)就是s.tab->fun[0](s.data)
。是不是和C++的虛表很像?但是他們有本質(zhì)的區(qū)別。先看C++,它為每個(gè)類(lèi)創(chuàng)建了一個(gè)方法集即虛表,當(dāng)子類(lèi)重寫(xiě)父類(lèi)的虛函數(shù)時(shí),就將表中的相應(yīng)函數(shù)指針改為子類(lèi)自己實(shí)現(xiàn)的函數(shù),如果沒(méi)有則指向父類(lèi)的實(shí)現(xiàn),當(dāng)面臨多繼承時(shí),C++對(duì)象結(jié)構(gòu)里就會(huì)存在多個(gè)虛表指針,每個(gè)虛表指針指向該方法集的不同部分。我們?cè)賮?lái)看golang的實(shí)現(xiàn)方式,同C++一樣,golang也為每種類(lèi)型創(chuàng)建了一個(gè)方法集,不同的是接口的虛表是在運(yùn)行時(shí)專(zhuān)門(mén)生成的,而c++的虛表是在編譯時(shí)生成的(但是c++虛函數(shù)表表現(xiàn)出的多態(tài)是在運(yùn)行時(shí)決定的).例如,當(dāng)例子中當(dāng)首次遇見(jiàn)s := Stringer(b)
這樣的語(yǔ)句時(shí),golang會(huì)生成Stringer接口對(duì)應(yīng)于Binary類(lèi)型的虛表,并將其緩存。那么為什么go不采用c++的方式來(lái)實(shí)現(xiàn)呢?這根c++和golang的對(duì)象內(nèi)存布局是有關(guān)系的。
首先c++的動(dòng)態(tài)多態(tài)是以繼承為基礎(chǔ)的,在對(duì)象構(gòu)造初始化的時(shí)首先會(huì)初始化父類(lèi),其次是子類(lèi),也就是說(shuō)一個(gè)對(duì)象的內(nèi)存布局是虛表,父類(lèi)部分,子類(lèi)部分(編譯器不同可能會(huì)有差異),當(dāng)一個(gè)父類(lèi)指針指向子類(lèi)時(shí),會(huì)發(fā)生內(nèi)存的截?cái)?,截?cái)嘧宇?lèi)部分(內(nèi)存地址偏移),但是此時(shí)子類(lèi)的虛表中的函數(shù)指針實(shí)際上還是指向了自己的實(shí)現(xiàn),所以此時(shí)的指針才會(huì)調(diào)用到子類(lèi)的虛函數(shù),如果不是虛函數(shù),因?yàn)閮?nèi)存已經(jīng)截?cái)鄾](méi)有子類(lèi)的非虛函數(shù)信息了,所以只能調(diào)用父類(lèi)的了,這種繼承關(guān)系讓c++的虛表的初始化非常清晰,在一個(gè)對(duì)象初始化時(shí)先調(diào)用父類(lèi)的構(gòu)造此時(shí)虛表跟父類(lèi)是一樣的,接下來(lái)初始化子類(lèi),此時(shí)編譯器就會(huì)去識(shí)別子類(lèi)有沒(méi)有覆蓋父類(lèi)的虛函數(shù),如果有則虛表中相應(yīng)的函數(shù)指針改成自己的虛函數(shù)實(shí)現(xiàn)指針。
那么go有什么不同呢,首先我們很清楚go是沒(méi)有嚴(yán)格意義上的繼承的,go的接口不存在繼承關(guān)系,只要實(shí)現(xiàn)了接口定義的方法都可以成為接口類(lèi)型,這給go的虛表初始化帶來(lái)很大的麻煩,到底有多少類(lèi)型實(shí)現(xiàn)了這個(gè)接口,一個(gè)類(lèi)型到底實(shí)現(xiàn)了多少接口這讓編譯器很confused。舉個(gè)例子,某個(gè)類(lèi)型有m個(gè)方法,某接口有n個(gè)方法,則很容易知道這種判定的時(shí)間復(fù)雜度為O(mXn),不過(guò)可以使用預(yù)先排序的方式進(jìn)行優(yōu)化,實(shí)際的時(shí)間復(fù)雜度為O(m+n)這樣看來(lái)其實(shí)還行那為什么要在運(yùn)行時(shí)生成虛表呢,這不是會(huì)拖慢程序的運(yùn)行速度嗎,注意我們這里是某個(gè)類(lèi)型,某個(gè)接口,是1對(duì)1的關(guān)系,如果有n個(gè)類(lèi)型,n個(gè)接口呢,編譯器難道要把之間所有的關(guān)系都理清嗎?退一步說(shuō)就算編譯器任勞任怨把這事干了,可是你在寫(xiě)過(guò)程中你本來(lái)就不想實(shí)現(xiàn)那個(gè)接口,而你無(wú)意中給這個(gè)類(lèi)型實(shí)現(xiàn)的方法中包含了某些接口的方法,你根本不需要這個(gè)接口(況且go的接口機(jī)制會(huì)導(dǎo)致很多這種無(wú)意義的接口實(shí)現(xiàn)),你欺負(fù)編譯器就行了,這也太欺負(fù)人了吧。如果我們放到運(yùn)行時(shí)呢,我們只要在需要接口的去分析一下類(lèi)型是否實(shí)現(xiàn)了接口的所有方法就行了很簡(jiǎn)單的一件事。
三 空接口
接口類(lèi)型的一個(gè)極端重要的例子是空接口:interface{}
,它表示空的方法集合,由于任何值都有零個(gè)或者多個(gè)方法,所以任何值都可以滿(mǎn)足它。 注意,[]T不能直接賦值給[]interface{}
//t := []int{1, 2, 3, 4} wrong //var s []interface{} = t t := []int{1, 2, 3, 4} //right s := make([]interface{}, len(t)) for i, v := range t { s[i] = v }
str, ok := value.(string) if ok { fmt.Printf("string value is: %q\n", str) } else { fmt.Printf("value is not a string\n") }
在Go語(yǔ)言中,我們可以使用type switch
語(yǔ)句查詢(xún)接口變量的真實(shí)數(shù)據(jù)類(lèi)型,語(yǔ)法如下:
type Stringer interface { String() string } var value interface{} // Value provided by caller. switch str := value.(type) { case string: return str //type of str is string case Stringer: //type of str is Stringer return str.String() }
也可以使用“comma, ok”的習(xí)慣用法來(lái)安全地測(cè)試值是否為一個(gè)字符串:
str, ok := value.(string) if ok { fmt.Printf("string value is: %q\n", str) } else { fmt.Printf("value is not a string\n") }
四 接口賦值
package main import ( "fmt" ) type LesssAdder interface { Less(b Integer) bool Add(b Integer) } type Integer int func (a Integer) Less(b Integer) bool { return a < b } func (a *Integer) Add(b Integer) { *a += b } func main() { var a Integer = 1 var b LesssAdder = &a fmt.Println(b) //var c LesssAdder = a //Error:Integer does not implement LesssAdder //(Add method has pointer receiver) }
go語(yǔ)言可以根據(jù)下面的函數(shù):
func (a Integer) Less(b Integer) bool
自動(dòng)生成一個(gè)新的Less()方法
func (a *Integer) Less(b Integer) bool
這樣,類(lèi)型*Integer就既存在Less()
方法,也存在Add()
方法,滿(mǎn)足LessAdder接口。 而根據(jù)
func (a *Integer) Add(b Integer)
這個(gè)函數(shù)無(wú)法生成以下成員方法:
func(a Integer) Add(b Integer) { (&a).Add(b) }
因?yàn)?code>(&a).Add()改變的只是函數(shù)參數(shù)a,對(duì)外部實(shí)際要操作的對(duì)象并無(wú)影響(值傳遞),這不符合用戶(hù)的預(yù)期。所以Go語(yǔ)言不會(huì)自動(dòng)為其生成該函數(shù)。因此類(lèi)型Integer只存在Less()方法,缺少Add()
方法,不滿(mǎn)足LessAddr接口。(可以這樣去理解:指針類(lèi)型的對(duì)象函數(shù)是可讀可寫(xiě)的,非指針類(lèi)型的對(duì)象函數(shù)是只讀的)將一個(gè)接口賦值給另外一個(gè)接口 在Go語(yǔ)言中,只要兩個(gè)接口擁有相同的方法列表(次序不同不要緊),那么它們就等同的,可以相互賦值。 如果A接口的方法列表時(shí)接口B的方法列表的子集,那么接口B可以賦值給接口A,但是反過(guò)來(lái)是不行的,無(wú)法通過(guò)編譯。
五 接口查詢(xún)
接口查詢(xún)是否成功,要在運(yùn)行期才能夠確定。他不像接口的賦值,編譯器只需要通過(guò)靜態(tài)類(lèi)型檢查即可判斷賦值是否可行。
var file1 Writer = ... if file5,ok := file1.(two.IStream);ok { ... }
這個(gè)if語(yǔ)句檢查file1接口指向的對(duì)象實(shí)例是否實(shí)現(xiàn)了two.IStream
接口,如果實(shí)現(xiàn)了,則執(zhí)行特定的代碼。
在Go語(yǔ)言中,你可以詢(xún)問(wèn)它指向的對(duì)象是否是某個(gè)類(lèi)型,比如,
var file1 Writer = ... if file6,ok := file1.(*File);ok { ... }
這個(gè)if語(yǔ)句判斷file1接口指向的對(duì)象實(shí)例是否是*File類(lèi)型,如果是則執(zhí)行特定的代碼。
slice := make([]int, 0) slice = append(slice, 1, 2, 3) var I interface{} = slice if res, ok := I.([]int);ok { fmt.Println(res) //[1 2 3] }
這個(gè)if語(yǔ)句判斷接口I所指向的對(duì)象是否是[]int類(lèi)型,如果是的話輸出切片中的元素。
func Sort(array interface{}, traveser Traveser) error { if array == nil { return errors.New("nil pointer") } var length int //數(shù)組的長(zhǎng)度 switch array.(type) { case []int: length = len(array.([]int)) case []string: length = len(array.([]string)) case []float32: length = len(array.([]float32)) default: return errors.New("error type") } if length == 0 { return errors.New("len is zero.") } traveser(array) return nil }
通過(guò)使用.(type)
方法可以利用switch來(lái)判斷接口存儲(chǔ)的類(lèi)型。
感謝各位的閱讀!關(guān)于“golang中interface接口怎么用”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,讓大家可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到吧!
本文標(biāo)題:golang中interface接口怎么用-創(chuàng)新互聯(lián)
分享網(wǎng)址:http://www.rwnh.cn/article10/cojjdo.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供全網(wǎng)營(yíng)銷(xiāo)推廣、靜態(tài)網(wǎng)站、網(wǎng)站制作、企業(yè)建站、ChatGPT、標(biāo)簽優(yōu)化
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容