關(guān)于 interface{} 會(huì)有啥注意事項(xiàng)?上
學(xué)習(xí) golang ,對(duì)于 interface{} 接口類(lèi)型,我們一定繞不過(guò),咱們一起來(lái)看看 使用 ?interface{} 的時(shí)候,都有哪些注意事項(xiàng)吧
interface {} 可以用于模擬多態(tài)
xdm 咱們寫(xiě)一個(gè)簡(jiǎn)單的例子,就舉動(dòng)物的例子
寫(xiě)一個(gè) Animal 的接口,類(lèi)似于 java 里面的抽象類(lèi) ,Animal 的接口 中有 2 個(gè)方案待實(shí)現(xiàn)
寫(xiě)一個(gè) Cat 來(lái)繼承 Animal , 實(shí)現(xiàn) Eat 方法和 Drink 方法
動(dòng)物都有吃和喝的行為,小貓吃的行為是吃魚(yú),小貓的喝的行為是喝可樂(lè)
最后在主函數(shù)中,使用父類(lèi)的指針,來(lái)指向子類(lèi)的實(shí)例化的一個(gè)子類(lèi)地址
type?Animal?interface?{
?Eat(string)?string
?Drink(string)?string
}
type?Cat?struct{}
func?(c?*Cat)?Eat(food?string)?string?{
?if?food?!=?"fish"?{
??return?"i?dislike"
?}?else?{
??return?"i?like"
?}
}
func?(c?*Cat)?Drink(drink?string)?string?{
?if?drink?==?"coke"?{
??return?"i?love"
?}else{
??return?"abandon"
?}
}
func?main(){
?var?a?Animal?=?&Cat{}
?fmt.Println(a.Eat("fish"))
?fmt.Println(a.Drink("water"))
}
看到上述代碼,會(huì)不會(huì)有這樣的疑問(wèn),命名是 &Cat{}
是取地址的,為什么 var a Animal
不寫(xiě)成指針呢?
這里需要注意,Animal 本身是 接口類(lèi)型,自身就是一個(gè)指針
運(yùn)行上述代碼查看效果
?go?run?main.go
i?like
abandon
沒(méi)有毛病,小貓瞇愛(ài)吃魚(yú),不愛(ài)喝水
interface{} 需要注意空和非空的情況
什么叫做空的 interface{} , ?什么又叫做非空的 interface{} 呢?
咱們還是用上面的例子, 添加一個(gè) testInterface 函數(shù)
,來(lái)實(shí)踐一下
func?testInterface()?Animal?{
?var?c?*Cat
?return?c
}
func?main()?{
?test?:=?testInterface()
?if?test?==?nil?{
??fmt.Println("test?is?nil")
?}?else?{
??fmt.Println("test?is?not?nil")
?}
}
可以猜猜看,上面這個(gè)小案例會(huì)輸出什么結(jié)果
理論上來(lái)看,
testInterface
函數(shù)中我們只是創(chuàng)建了一個(gè) Cat 指針,并沒(méi)有賦值,因此默認(rèn)是一個(gè)零值,因此會(huì)是一個(gè) nil,那么 return 的時(shí)候,應(yīng)該也是 return nil 才對(duì)吧,因此按照代碼的邏輯來(lái)說(shuō)應(yīng)該是輸出 ?test is nil
執(zhí)行上述代碼后,查看結(jié)果
?go?run?main.go
test?is?not?nil
看到上面的結(jié)果,是不是覺(jué)得很奇怪,和自己的預(yù)期不一致
沒(méi)關(guān)系,之前的文章我們說(shuō)到過(guò),覺(jué)得一個(gè)技術(shù)點(diǎn)奇怪,不是我們所期望的效果,原因是我們對(duì)其原理不夠了解,不夠熟悉
現(xiàn)在先來(lái)回答一下上面的問(wèn)題
空接口:意思是沒(méi)有方法的接口,interface{} 源碼中表示為 ? eface 結(jié)構(gòu)體
非空接口:表示有包含方法的接口 , interface{} 源碼中表示為 ? iface 結(jié)構(gòu)體
暫時(shí)先來(lái)直接介紹源碼中的結(jié)構(gòu)體
iface 結(jié)構(gòu)體 , 非空
type?iface?struct?{
?tab??*itab
?data?unsafe.Pointer
}
type?itab?struct?{
?inter??*interfacetype
?_type??*_type
?link???*itab
?hash???uint32?//?copy?of?_type.hash.?Used?for?type?switches.
?bad????bool???//?type?does?not?implement?interface
?inhash?bool???//?has?this?itab?been?added?to?hash?
?unused?[2]byte
?fun????[1]uintptr?//?variable?sized
}
tab
指的是具體的類(lèi)型信息,是一個(gè) ?itab 結(jié)構(gòu),結(jié)構(gòu)中成員如上,這里面包含的都是借口的關(guān)鍵信息,例如 hash 值 ,函數(shù)指針,等等,后續(xù)詳細(xì)剖析 interface{} 原理的時(shí)候再統(tǒng)一說(shuō)
data
具體的數(shù)據(jù)信息
eface 結(jié)構(gòu)體
type?eface?struct?{
????_type?*_type
????data??unsafe.Pointer
}type?_type?struct?{
????size???????uintptr??//?表示的是?類(lèi)型的大小
????ptrdata????uintptr??//?值的是前綴指針的內(nèi)存大小
????hash???????uint32???//?計(jì)算數(shù)據(jù)的?hash?值
????tflag??????tflag
????align??????uint8????//??進(jìn)行內(nèi)存對(duì)齊的
????fieldalign?uint8?
????kind???????uint8?
????alg????????*typeAlg?
????gcdata????*byte
????str???????nameOff
????ptrToThis?typeOff
}
_type
類(lèi)型信息,和上面的 非空接口類(lèi)似 , 這個(gè)_type 類(lèi)型決定下面的 data 字段如何去解釋數(shù)據(jù)
data
具體的數(shù)據(jù)信息
看到這里,細(xì)心的 xdm 是不是就可以看出來(lái),我們上面寫(xiě)的 Animal 接口,其實(shí)是一個(gè)非空接口,因?yàn)槔锩嬗邪椒ǎ?strong>所以他的底層是一個(gè) iface 結(jié)構(gòu)體 ?,非空接口
那么初始化的一個(gè)空指針 c ,實(shí)際上是 iface 結(jié)構(gòu)體里面的 data 字段為空而已,數(shù)據(jù)為空而已,但是 iface 這個(gè)結(jié)構(gòu)體自己不是空的,所以上述代碼走的邏輯是 ?test is not nil
這里順帶說(shuō)一下,golang 中,還有哪些數(shù)據(jù)結(jié)構(gòu)是和 nil 比較是否為零值,這個(gè)點(diǎn)我們也可以看看源碼
//?nil?is?a?predeclared?identifier?representing?the?zero?value?for?a
//?pointer,?channel,?func,?interface,?map,?or?slice?type.
var?nil?Type?//?Type?must?be?a?pointer,?channel,?func,?interface,?map,?or?slice?type
源碼中有說(shuō)到,可以對(duì) 指針,通道,函數(shù),接口,map,切片類(lèi)型使用 ?nil
好了,本次就到這里,知識(shí)點(diǎn)要用起來(lái)才有價(jià)值
歡迎點(diǎn)贊,關(guān)注,收藏
朋友們,你的支持和鼓勵(lì),是我堅(jiān)持分享,提高質(zhì)量的動(dòng)力
好了,本次就到這里
技術(shù)是開(kāi)放的,我們的心態(tài),更應(yīng)是開(kāi)放的。擁抱變化,向陽(yáng)而生,努力向前行。
我是阿兵云原生,歡迎點(diǎn)贊關(guān)注收藏,下次見(jiàn)~