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 *_type
data unsafe.Pointer
}
所以即使两个变量都是interface{}
类型,但底层的类型不同,则两个变量不相等
var (
a interface{} = 123
b 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)
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)