學習 golang ,對於 interface{} 介面型別,我們一定繞不過,我們們一起來看看 使用 interface{} 的時候,都有哪些注意事項吧
interface {} 可以用於模擬多型
xdm 我們們寫一個簡單的例子,就舉動物的例子
寫一個 Animal 的介面,類似於 java 裡面的抽象類 ,Animal 的介面 中有 2 個方案待實現
寫一個 Cat 來繼承 Animal , 實現 Eat 方法和 Drink 方法
- 動物都有吃和喝的行為,小貓吃的行為是吃魚,小貓的喝的行為是喝可樂
- 最後在主函式中,使用父類的指標,來指向子類的例項化的一個子類地址
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"))
}
看到上述程式碼,會不會有這樣的疑問,命名是 &Cat{}
是取地址的,為什麼 var a Animal
不寫成指標呢?
這裡需要注意,Animal 本身是 介面型別,自身就是一個指標
執行上述程式碼檢視效果
# go run main.go
i like
abandon
沒有毛病,小貓眯愛吃魚,不愛喝水
interface{} 需要注意空和非空的情況
什麼叫做空的 interface{} , 什麼又叫做非空的 interface{} 呢?
我們們還是用上面的例子, 新增一個 testInterface 函式
,來實踐一下
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")
}
}
可以猜猜看,上面這個小案例會輸出什麼結果
- 理論上來看,
testInterface
函式中我們只是建立了一個 Cat 指標,並沒有賦值,因此預設是一個零值,因此會是一個 nil,那麼 return 的時候,應該也是 return nil 才對吧,因此按照程式碼的邏輯來說應該是輸出test is nil
執行上述程式碼後,檢視結果
# go run main.go
test is not nil
看到上面的結果,是不是覺得很奇怪,和自己的預期不一致
沒關係,之前的文章我們說到過,覺得一個技術點奇怪,不是我們所期望的效果,原因是我們對其原理不夠了解,不夠熟悉
現在先來回答一下上面的問題
空介面:意思是沒有方法的介面,interface{} 原始碼中表示為 eface 結構體
非空介面:表示有包含方法的介面 , interface{} 原始碼中表示為 iface 結構體
暫時先來直接介紹原始碼中的結構體
iface 結構體 , 非空
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
指的是具體的型別資訊,是一個 itab 結構,結構中成員如上,這裡麵包含的都是藉口的關鍵資訊,例如 hash 值 ,函式指標,等等,後續詳細剖析 interface{} 原理的時候再統一說
- data
具體的資料資訊
eface 結構體
type eface struct {
_type *_type
data unsafe.Pointer
}
type _type struct {
size uintptr // 表示的是 型別的大小
ptrdata uintptr // 值的是字首指標的記憶體大小
hash uint32 // 計算資料的 hash 值
tflag tflag
align uint8 // 進行記憶體對齊的
fieldalign uint8
kind uint8
alg *typeAlg
gcdata *byte
str nameOff
ptrToThis typeOff
}
- _type
型別資訊,和上面的 非空介面類似 , 這個_type 型別決定下面的 data 欄位如何去解釋資料
- data
具體的資料資訊
看到這裡,細心的 xdm 是不是就可以看出來,我們上面寫的 Animal 介面,其實是一個非空介面,因為裡面有包含方法,所以他的底層是一個 iface 結構體 ,非空介面
那麼初始化的一個空指標 c ,實際上是 iface 結構體裡面的 data 欄位為空而已,資料為空而已,但是 iface 這個結構體自己不是空的,所以上述程式碼走的邏輯是 test is not nil
這裡順帶說一下,golang 中,還有哪些資料結構是和 nil 比較是否為零值,這個點我們也可以看看原始碼
// 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
原始碼中有說到,可以對 指標,通道,函式,介面,map,切片型別使用 nil
好了,本次就到這裡,知識點要用起來才有價值
歡迎點贊,關注,收藏
朋友們,你的支援和鼓勵,是我堅持分享,提高質量的動力
好了,本次就到這裡
技術是開放的,我們的心態,更應是開放的。擁抱變化,向陽而生,努力向前行。
我是阿兵雲原生,歡迎點贊關注收藏,下次見~