理解Golang中的[]interface{}和interface{}
理解Golang中的[]interface{}和interface{}
原文鏈接: 理解Golang中的[]interface{}和interface{}
之前在開(kāi)發(fā)Go項(xiàng)目操作Redis時(shí),利用Do函數(shù)進(jìn)行數(shù)據(jù)操作,在返回的interface{}類(lèi)型的轉(zhuǎn)換中踩了一個(gè)大坑。
Do(ctx, "HKEYS", "KEY")在閱讀源碼中發(fā)現(xiàn),Do方法的是一個(gè)[]interface{}切片
func (c *Redis) Do(ctx context.Context, commandName string, args ...interface{}) (interface{}, error) {//// ...//reply, err := conn.Do(commandName, args...)//// ...//return reply, c.err } func (c *conn) Do(cmd string, args ...interface{}) (interface{}, error) {return c.DoWithTimeout(c.readTimeout, cmd, args...) } func (c *conn) DoWithTimeout(readTimeout time.Duration, cmd string, args ...interface{}) (interface{}, error) {//// ...//reply := make([]interface{}, pending)//// ...//return reply, err }在Goland中有一種特殊類(lèi)型:interface{} ,空接口。interface{} 類(lèi)型是沒(méi)有方法的接口。由于沒(méi)有 implements 關(guān)鍵字,所以所有類(lèi)型都至少實(shí)現(xiàn)了 0 個(gè)方法,所以 所有類(lèi)型都實(shí)現(xiàn)了空接口。這意味著,如果編寫(xiě)一個(gè)函數(shù)以 interface{} 值作為參數(shù),那么可以為該函數(shù)提供任何值,并且,[]interface{}在golang中也可以認(rèn)為是interface{}
func main() {method("string")method(123)method(make(map[string]int))method(make([]interface{}, 0))method(true) }func method(arg interface{}) {}所以Do方法的返回值是interface{}類(lèi)型,但本質(zhì)上應(yīng)該是[]interface{}類(lèi)型,又因?yàn)閞edis的hkeys操作返回的是field字符串?dāng)?shù)組
那么上述命令的返回值實(shí)際上應(yīng)該是[]string或者[]byte類(lèi)型,于是利用golang的類(lèi)型判定,寫(xiě)出了如下代碼
func main() {var a interface{} = method()bytes := a.([]byte)fmt.Println(bytes) }func method() interface{} {ans := make([]interface{}, 0)return append(ans, []byte{96, 97, 98, 99}) }然而,編譯器狠狠的打了我的臉
既然interface{}能代表任意類(lèi)型,那么interface{}的切片為什么不能代表任意類(lèi)型的切片呢?
了解了相關(guān)底層數(shù)據(jù)存儲(chǔ)原理后,這個(gè)問(wèn)題也就迎刃而解了
一個(gè)interface{}類(lèi)型的變量的底層存儲(chǔ)結(jié)構(gòu)由兩個(gè)字word組成;一個(gè)字用于指向該值底層類(lèi)型的方法表,另一個(gè)字用于指向?qū)嶋H數(shù)據(jù)
type eface struct {_type *_typedata unsafe.Pointer }所以即使兩個(gè)變量都是interface{}類(lèi)型,但底層的類(lèi)型不同,則兩個(gè)變量不相等
var (a interface{} = 123b interface{} = "string" ) fmt.Println(a == b) // false那么對(duì)于[]interface{}類(lèi)型的變量來(lái)說(shuō),切片里的每個(gè)元素可以存儲(chǔ)不同類(lèi)型的變量,例如
func main() {var a = make([]interface{}, 0)a = append(a, []int{123, 456})a = append(a, []string{"abc", "ijk"})fmt.Println(a) // [[123 456] [abc ijk]] }但即使切片里存的數(shù)據(jù)都是某個(gè)特定的類(lèi)型,也不能通過(guò)類(lèi)型斷定來(lái)強(qiáng)制轉(zhuǎn)換,因?yàn)榈讓拥臄?shù)據(jù)存儲(chǔ)結(jié)構(gòu)不同
func main() {a := method()_, ok := a.([]int)fmt.Println(ok) // false }func method() interface{} {var a = make([]interface{}, 0)a = append(a, []int{123, 456})a = append(a, []int{789, 111})return a }Each interface{} takes up two words (one word for the type of what is contained, the other word for either the contained data or a pointer to it). As a consequence, a slice with length N and with type []interface{} is backed by a chunk of data that is N*2 words long.
This is different than the chunk of data backing a slice with type []MyType and the same length. Its chunk of data will be N*sizeof(MyType) words long.
The result is that you cannot quickly assign something of type []MyType to something of type []interface{}; the data behind them just look different.
那么如果我們要把同類(lèi)型組成的切片轉(zhuǎn)換成的特定類(lèi)型,可以這樣做
func main() {a := method()ans := make([][]int, 0)b, ok := a.([]interface{})if ok {for _, element := range b {ans = append(ans, element.([]int))}}fmt.Println(ans) // [[123 456] [789 111]] }func method() interface{} {var a = make([]interface{}, 0)a = append(a, []int{123, 456})a = append(a, []int{789, 111})return a }參考文章:
InterfaceSlice · golang/go Wiki (github.com)
總結(jié)
以上是生活随笔為你收集整理的理解Golang中的[]interface{}和interface{}的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Ghelper安装及使用
- 下一篇: 4-2 webpack使用mapsour