go 类型断言_深入理解Go的interface内部执行原理
Go的interface是由两种类型来实现的: iface 和 eface
iface指的是接口中申明有方法(至少1个),eface表示接口中没有申明方法
后面会讲到这两个到底是什么,所以这里需要先不用关心。
深入理解
下面是一个简单的Demo,Binary实现了fmt.Stringer接口,我们调用 ToString() 方法,会调用接口的 String() 方法。
// 类型type Binary uint64// 实现String方法,实现fmt.Stringer接口func (i Binary) String() string {return strconv.FormatUint(uint64(i), 10)}func main() {b := Binary(200) // 01 // var b Binary = Binary(200)ToString(b)// 02}func ToString(value interface{}) string { // 断言,转化成fmt.Stringer接口类型newValue, ok := value.(fmt.Stringer) //3if ok {return newValue.String()// 4}panic("The value is not implement fmt.Stringer func")}
大致的执行流程图下所示:
//01 执行的是,在内存中开辟一块内存,存放200这个值
![](/assets/blank.gif)
// 02 调用ToString方法,首先方法传递过程中需要隐式将b转换成interface{}类型,实际上做的就是以下:
![](/assets/blank.gif)
首先大家可能会关心,我就没见过这个结构,你是不是骗人的,其实有这个结构体,是在 runtime/runtime2.go 中
type eface struct {_type *_type // 类型data unsafe.Pointer //值}
那么如何转换的呢?
type 指得是 Binary的类型,包含了Binary类型的所有信息(后面会介绍到)
data 指向的真实数据,由于我们传递的不是指针,所以这种情况下其实是做了一次内存拷贝( 所以也就是尽可能的别使用interface{} ),data其实存的是拷贝的数据,如果换做是指针,其实也是拷贝了一份指针地址(这也就是reflect.Elem方法的作用)
以下这几段代码全部来自于 runtime/iface.go
// 关于 unsafe.Pointer,unsafe包学习的时候介绍过func convT2E(t *_type, elem unsafe.Pointer) (e eface) {if raceenabled {raceReadObjectPC(t, elem, getcallerpc(), funcPC(convT2E))}if msanenabled {msanread(elem, t.size)} // 首先会分配一块内存,内存大小为类型t的大小,下面这段话是mallocgc的介绍 // Allocate an object of size bytes. // Small objects are allocated from the per-P cache's free lists. // Large objects (> 32 kB) are allocated straight from the heap.x := mallocgc(t.size, t, true)// TODO: We allocate a zeroed object only to overwrite it with actual data.// Figure out how to avoid zeroing. Also below in convT2Eslice, convT2I, convT2Islice. // 将elem拷贝到x typedmemmove(t, x, elem) // eface 的类型为t,值为xe._type = te.data = xreturn}
//3 其次就是到了断言的部分,那么断言到底执行了什么呢?
// inter 指的是fmt.Stringer接口类型信息// e 就是我们上面的的interface{} 的真实类型efacefunc assertE2I2(inter *interfacetype, e eface) (r iface, b bool) {t := e._typeif t == nil {return} // 获取tab,其实大家有可能不太理解tab := getitab(inter, t, true)if tab == nil {return}r.tab = tabr.data = e.datab = truereturn}
那么这里就需要理解什么是 iface
type iface struct {tab *itab//tabledata unsafe.Pointer //值}
tab 又是什么?
tab的意思是table的意思,关于table的概念,大家可以去找找资料 具有方法的语言通常属于以下两种阵营之一:为所有方法调用静态地准备表(如在C ++和Java中),或在每次调用时进行方法查找(如在Smalltalk及其许多模仿程序中,包括JavaScript和Python)以及添加奇特的缓存以提高调用效率。Go位于两者的中间:它具有方法表,但在运行时对其进行计算。
type itab struct {inter *interfacetype// 接口类型,这里就是Stringer_type *_type// 值类型,这里就是Binaryhash uint32 // copy of _type.hash. Used for type switches._ [4]bytefun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter.}
其实上面这段代码的流程如下:
data就是 eface.data
tab 其实就是 : inter 指的是接口类型(也就是fmt.Stringer接口), type 是Binary类型, fun[0] 是(Binary)String方法 ,其他几个先不用care
![](/assets/blank.gif)
//4 newValue.String() 到底做了啥,其实根据上面我们很容易知道,无法就是 newValue.tab.fun[0].(newValue.data) ,所以就是这么简单。
总结
1、go的 interface{} 转换过程中至少做一次内存拷贝,所以传递指针是最好的选择。
type User struct {Name stringAge int}func main() { var empty interface{} = User{} //这里会拷贝一次,将user转换成interface{},所以函数传递过程中也别直接使用结构体传递}
正确写法
func main() {var empty interface{} = &User{}}
2、有人会问到字符串传递是否内存拷贝,回答否,因为字符串底层是一个 byte[] 数组,他的结构组成是
type StringHeader struct {Data uintptr //数据,是一个二进制数组Len int// 长度}
所以64位计算机,字符串的长度是128位,占用16个字节
3、减少使用 interface{} ,因为会有不必要的开销,其次Golang本身是一个强类型语言,静态语言,申明式是最好的方式。
4、 interface{} 是反射的核心,后期我会讲解反射
go 类型断言_深入理解Go的interface内部执行原理相关推荐
- go interface 转 string_深入理解Go的interface内部执行原理
Go的interface是由两种类型来实现的: iface 和 eface iface指的是接口中申明有方法(至少1个),eface表示接口中没有申明方法 后面会讲到这两个到底是什么,所以这里需要先不 ...
- go 类型断言_(57)接口的类型断言
GO提供了一个方法,用来判断接口的底层值是什么类型 类型断言 提供了访问接口值底层具体值的方式. t := i.(T) 该语句断言接口值 i 保存了具体类型 T,并将其底层类型为 T 的值赋予变量 t ...
- factorybean 代理类不能按照类型注入_快速理解Spring中的FactoryBean接口
1.前提概要 很多java开发者在使用Spring框架中都见过后缀为FactoryBean的类,比如Mybatis-Spring中的SqlSessionFactoryBean.说到这里就不得不提Bea ...
- 在python中类型属于对象变量是没有类型的_如何理解python对象有类型,变量无类型...
在Python中,有这样一句话是非常重要的:对象有类型,变量无类型.怎么理解呢? 首先,5.6都是整数,Python中为它们取了一个名字,叫做"整数"类型的对象(或者数据),也可以 ...
- jvm对象从新生代到老年代_深入理解jvm内存模型以及gc原理
整体架构 Jvm = 类加载器 + 执行引擎 + 运行时数据区域 类加载器 ● 作用 类加载器是将编译好的class文件加载到内存中,并进行验证.初始化等步骤,形成能被jvm直接使用的类型. ● 加载 ...
- java逻辑第九章_深入理解jvm-(第九章)类加载及执行子系统的案例与实战
转载自:http://blog.csdn.net/coslay/article/details/49564789 概述 在Class文件格式与执行引擎这部分中,用户的程序能直接影响的内容并不太多, C ...
- springcloud 组件_深入理解 Spring Cloud 核心组件与底层原理
新人大礼包,30G Java架构资料,免费领取zhuanlan.zhihu.com 一.Spring Cloud核心组件:Eureka Netflix Eureka Eureka详解 1.服务提供者 ...
- java调用子系统代码_深入理解JAVA虚拟机-Idea远程执行本地Java代码 - Java 技术驿站-Java 技术驿站...
今天在看深入理解JAVA虚拟机的9.3节,作者实现了一个远程执行功能.这个功能可以在远程服务器中临时执行一段程序代码,而去不依赖jdk版本,不改变原有服务端程序的部署,不依赖任何第三方库,不入侵原有的 ...
- acid四大特性_深入理解MySQL的ACID四大特性原理
专注于Java领域优质技术,欢迎关注 作者:孤独烟 引言 照例,我们先来一个场景~ 面试官:"知道事务的四大特性么?" 你:"懂,ACID嘛,原子性(Atomicity) ...
最新文章
- TensorFlow损失函数
- 安卓串口中InputStream数据接收不完整
- ArcGIS水文分析实战教程(9)雨量计算与流量统计
- 端口聚合与Trunk综合配置
- 【ThinkPHP系列篇】ThinkPHP框架的介绍和搭建(一)
- Python最快的方式来读取大文本文件(几GB)
- linux改变时间 find,Find命令查找最近几天修改的文件
- setuptools Command Reference
- 从科大讯飞跳槽到腾讯被判赔 1200 万;华为多款机型将取消充电器;苹果春季发布会定档4月21日|极客头条...
- js 正则 或_一次记住js的6个正则方法
- 初步探究ES6之箭头函数
- 华为p8升级android8系统资源,华为畅享8官方固件rom刷机包_畅享8完整版系统升级包下载...
- ImageJ如何获取图片RGB强度和灰度值
- Ubuntu20.04 设置虚拟内存
- 抖音小程序Tiktok开发教程之 基础组件 04 icon 图标组件
- python自动化(六)持续集成:2.Jenkins技术讲解
- 好喝的阿拉伯咖啡Gahwa
- 卷积神经网络图像分类的性能评估指标有哪些
- Mybatis开启驼峰命名,作用
- Electron渲染页面(Renderer Process)引入ipcRenderer
热门文章
- linux link path walk,python之os.walk()与os.path.walk()
- java project保存_java project 导出介绍
- linux一次性密码确保ssh登录安全,使用 SSH 时确保 EC2 Linux 实例安全的最佳实践
- 表单多条相同name数据的获取
- thinkPHP定义路由
- [Ubuntu18.04]使用snap
- nyoj8-一种排序
- HDU 4389——X mod f(x)(数位DP)
- error LNK2001: 无法解析的外部符号 __iob_func
- WSL自定义安装路径