理解Golang中的[]interface{}和interface{}
理解Golang中的[]interface{}和interface{}
原文鏈接: 理解Golang中的[]interface{}和interface{}
之前在開發Go項目操作Redis時,利用Do函數進行數據操作,在返回的interface{}類型的轉換中踩了一個大坑。
Do(ctx, "HKEYS", "KEY")在閱讀源碼中發現,Do方法的是一個[]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中有一種特殊類型:interface{} ,空接口。interface{} 類型是沒有方法的接口。由于沒有 implements 關鍵字,所以所有類型都至少實現了 0 個方法,所以 所有類型都實現了空接口。這意味著,如果編寫一個函數以 interface{} 值作為參數,那么可以為該函數提供任何值,并且,[]interface{}在golang中也可以認為是interface{}
func main() {method("string")method(123)method(make(map[string]int))method(make([]interface{}, 0))method(true) }func method(arg interface{}) {}所以Do方法的返回值是interface{}類型,但本質上應該是[]interface{}類型,又因為redis的hkeys操作返回的是field字符串數組
那么上述命令的返回值實際上應該是[]string或者[]byte類型,于是利用golang的類型判定,寫出了如下代碼
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{}能代表任意類型,那么interface{}的切片為什么不能代表任意類型的切片呢?
了解了相關底層數據存儲原理后,這個問題也就迎刃而解了
一個interface{}類型的變量的底層存儲結構由兩個字word組成;一個字用于指向該值底層類型的方法表,另一個字用于指向實際數據
type eface struct {_type *_typedata unsafe.Pointer }所以即使兩個變量都是interface{}類型,但底層的類型不同,則兩個變量不相等
var (a interface{} = 123b interface{} = "string" ) fmt.Println(a == b) // false那么對于[]interface{}類型的變量來說,切片里的每個元素可以存儲不同類型的變量,例如
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]] }但即使切片里存的數據都是某個特定的類型,也不能通過類型斷定來強制轉換,因為底層的數據存儲結構不同
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.
那么如果我們要把同類型組成的切片轉換成的特定類型,可以這樣做
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)
總結
以上是生活随笔為你收集整理的理解Golang中的[]interface{}和interface{}的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ghelper安装及使用
- 下一篇: 4-2 webpack使用mapsour