理解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{}相关推荐

  1. 理解Golang中的nil

    参考: 有趣的面试题:Go语言中的nil比较 - 知乎 (zhihu.com) 理解Go语言的nil - 简书 (jianshu.com) Golang中的nil,没有人比我更懂nil! - 知乎 ( ...

  2. 深入理解Golang中的Context包

    context.Context是Go语言中独特的设计,在其他编程语言中我们很少见到类似的概念.context.Context深度支持Golang的高并发. 1. Goroutine和Channel 在 ...

  3. golang 切片 接口_如何理解Golang中的接口?

    个人认为,要理解 Go 的接口,一定先了解下鸭子模型. 鸭子模型 那什么鸭子模型? 鸭子模型的解释,通常会用了一个非常有趣的例子,一个东西究竟是不是鸭子,取决于它的能力.游泳起来像鸭子.叫起来也像鸭子 ...

  4. 理解golang中什么是nil

    nil是什么 // nil is a predeclared identifier representing the zero value for a // pointer, channel, fun ...

  5. 理解Golang中defer的使用

    之前一直对Go中的defer不太理解,所以我单独弄出来整理一下. 在golang当中,defer代码块会在函数调用链表中增加一个函数调用.这个函数调用不是普通的函数调用,而是会在函数正常返回,也就是r ...

  6. golang语言 []interface{}和interface{}

    文章目录 golang语言 []interface{} interface(接口) interface应用场景 interface{} 空接口 []interface{} golang中为什么[]st ...

  7. golang 接口_「Golang系列」 深入理解Golang Empty Interface (空接口)

    空接口可用于保存任何数据,它可以是一个有用的参数,因为它可以使用任何类型. 要理解空接口如何工作以及如何保存任何类型,我们首先应该理解名称背后的概念. 接口 这是Jordan Oreilli对空接口的 ...

  8. golang 编译提示 cannot assign interface {} 和golang断言使用

    golang 编译提示 cannot assign interface {} 和golang断言使用: 从sync.Map中读取值的时候出现如下编译错误,因为返回的是interface类型,需要做转换 ...

  9. Go语言-switch case | switch中判断多个值、interface conversion: interface {} is float64, not int

    文章目录 Go语言-switch case 背景 switch case Golang中switch的特殊用法--fallthrough go语言switch中判断多个值 Type Switch,判断 ...

  10. java中的abstract和interface差异

    java中的abstract和interface差异 这里需要谈到抽象类和Interface的差别,abstract class和interface是支持抽象类定义的两种机制.正是由于这两种机制的存在 ...

最新文章

  1. c# 使用dotnetbar 控件绘制曲线图形
  2. 使用Linux建立拨号服务器
  3. Python高阶函数和eval函数
  4. windows下SecureCRT无法使用backspace(空格键)和上下左右键
  5. 物联网将成为第四次工业革命的基石
  6. mysql 优化的一些小窍门
  7. 手机端页面自适应解决方案-rem布局
  8. ElasticSearch全文搜索引擎之入门以及环境搭建
  9. 根据excel生成mysql数据库,根据excel生成数据库脚本
  10. 顶级知识管理工具Evernote的GTD应用详细指南
  11. QT多线程,使用串口接收数据通过UDP端口进行数据转发
  12. ajax回调函数运用(由前端到后端代码全过程)
  13. tf.nn.tanh 双曲正切曲线
  14. matlab 阻尼牛顿法
  15. PLSQL的快捷键以及使用技巧
  16. 魔域进游戏老是显示服务器繁忙,魔域2014年春节-温情卡诺萨
  17. React Api请求最佳实践react-query3使用教程(比swr更好用更强大)
  18. Mysql utf8mb3 utf8mb4 与UTF8 字符集参数(character_set_system)的说明
  19. 玩电脑玩出的 Linux 专家 - Google(谷歌)开发者日演讲者之苏哲
  20. FutureNet NXR-G100路由器

热门文章

  1. “梦回三国”系列总结
  2. JS_综合,全面性增删改查,多条件查询,排序,点击发货
  3. 许晓斌_Maven实战(五)——自动化Web应用集成测试
  4. 一个人越想挣钱,越挣不到钱!背后的原因是什么?
  5. 2022年新型智慧城市整体规划建设方案
  6. 区块链Baas应用服务平台开发搭建
  7. 千峰商城-springboot项目搭建-06-数据库创建
  8. 使用SQLite Developer可视化解析sqlite数据库文件
  9. 使用Supermemo背单词6周年了
  10. 期权定价_强化学习的期权定价