从零学习 Go 语言(35):图解 Go 语言 静态类型与动态类型

hello_wbm · · 1009 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

![](http://image.iswbm.com/20200607145423.png) 在线博客:http://golang.iswbm.com/ Github:https://github.com/iswbm/GolangCodingTime --- ## 1. 静态类型 所谓的**静态类型**(即 static type),就是变量声明的时候的类型。 ```go var age int // int 是静态类型 var name string // string 也是静态类型 ``` 它是你在编码时,肉眼可见的类型。 ## 2. 动态类型 所谓的 **动态类型**(即 concrete type,也叫具体类型)是 程序运行时系统才能看见的类型。 这是什么意思呢? 我们都知道 **空接口** 可以承接什么问题类型的值,什么 int 呀,string 呀,都可以接收。 比如下面这几行代码 ```go var i interface{} i = 18 i = "Go编程时光" ``` 第一行:我们在给 `i` 声明了 `interface{}` 类型,所以 `i` 的静态类型就是 `interface{}` 第二行:当我们给变量 `i` 赋一个 int 类型的值时,它的静态类型还是 interface{},这是不会变的,但是它的动态类型此时变成了 int 类型。 第三行:当我们给变量 `i` 赋一个 string 类型的值时,它的静态类型还是 interface{},它还是不会变,但是它的动态类型此时又变成了 string 类型。 从以上,可以知道,不管是 `i=18` ,还是 `i="Go编程时光"`,都是当程序运行到这里时,变量的类型,才发生了改变,这就是我们最开始所说的 动态类型是程序运行时系统才能看见的类型。 ## 3. 接口组成 每个接口变量,实际上都是由一 pair 对(type 和 data)组合而成,pair 对中记录着实际变量的值和类型。 比如下面这条语句 ```go var age int = 25 ``` 我们声明了一个 int 类型变量,变量名叫 age ,其值为 25 ![](http://image.iswbm.com/20200610235106.png) 知道了接口的组成后,我们在定义一个变量时,除了使用常规的方法(可参考:[**02. 学习五种变量创建的方法**](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=2&sn=e70a1400c094e981f15b8da552bd8fbf&chksm=fc355b7ecb42d26824985163a3ef0c3567134975637c4efc42161751f54ab10343b485b36e23&scene=21#wechat_redirect)) 也可以使用像下面这样的方式 ```go package main import "fmt" func main() { age := (int)(25) //或者使用 age := (interface{})(25) fmt.Printf("type: %T, data: %v ", age, age) } ``` 输出如下 ```go type: int, data: 25 ``` ## 4. 接口细分 根据接口是否包含方法,可以将接口分为 `iface` 和 `eface`。 ### iface 第一种:**iface**,表示带有一组方法的接口。 比如 ```go type Phone interface { call() } ``` `iface` 的具体结构可用如下一张图来表示 ![iface 结构](http://image.iswbm.com/20200610220830.png) iface 的源码如下: ```go // runtime/runtime2.go // 非空接口 type iface struct { tab *itab data unsafe.Pointer } // 非空接口的类型信息 type itab struct { inter *interfacetype // 接口定义的类型信息 _type *_type // 接口实际指向值的类型信息 link *itab bad int32 inhash int32 fun [1]uintptr // 接口方法实现列表,即函数地址列表,按字典序排序 } // runtime/type.go // 非空接口类型,接口定义,包路径等。 type interfacetype struct { typ _type pkgpath name mhdr []imethod // 接口方法声明列表,按字典序排序 } // 接口的方法声明 type imethod struct { name nameOff // 方法名 ityp typeOff // 描述方法参数返回值等细节 } ``` ### eface 第二种:**eface**,表示不带有方法的接口 比如 ```go var i interface{} ``` eface 的源码如下: ```go // src/runtime/runtime2.go // 空接口 type eface struct { _type *_type data unsafe.Pointer } ``` ![eface 结构组成](http://image.iswbm.com/20200610221213.png) ## 5.理解动态类型 前两节,我们知道了什么是动态类型?如何让一个对象具有动态类型? 后两节,我们知道了接口分两种,它们的内部结构各是什么样的? 那最后一节,可以将前面四节的内容结合起来,看看在给一个空接口类型的变量赋值时,接口的内部结构会发生怎样的变化 。 ### iface 先来看看 iface,有如下一段代码: ```go var reader io.Reader tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } reader = tty ``` 第一行代码:var reader io.Reader ,由于 io.Reader 接口包含 Read 方法,所以 io.Reader 是 `iface`,此时 reader 对象的静态类型是 io.Reader,暂无动态类型。 ![](http://image.iswbm.com/image-20200610225323018.png) 最后一行代码:reader = tty,tty 是一个 `*os.File` 类型的实例,此时reader 对象的静态类型还是 io.Reader,而动态类型变成了 `*os.File`。 ![](http://image.iswbm.com/20200610230951.png) ### eface 再来看看 eface,有如下一段代码: ```go //不带函数的interface var empty interface{} tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } empty = tty ``` 第一行代码:var empty interface{},由于 `interface{}` 是一个 eface,其只有一个 `_type` 可以存放变量类型,此时 empty 对象的(静态)类型是 nil。 ![](http://image.iswbm.com/image-20200610230819030.png) 最后一行代码:empty = tty,tty 是一个 `*os.File` 类型的实例,此时 `_type` 变成了 `*os.File`。 ![](http://image.iswbm.com/image-20200610231015612.png) ## 6. 反射的必要性 由于动态类型的存在,在一个函数中接收的参数的类型有可能无法预先知晓,此时我们就要对参数进行反射,然后根据不同的类型做不同的处理。 关于 反射 的内容有点多,我将其安排在下一篇。 ## 参考文章 - [图解go反射实现原理](https://i6448038.github.io/2020/02/15/golang-reflection/) ## 系列导读 --- [从零学习 Go 语言(01):一文搞定开发环境的搭建](https://studygolang.com/articles/27365) [从零学习 Go 语言(02):学习五种变量创建的方法](https://studygolang.com/articles/27432) [从零学习 Go 语言(03):数据类型之整型与浮点型](https://studygolang.com/articles/27440) [从零学习 Go 语言(04):byte、rune与字符串](https://studygolang.com/articles/27463) [从零学习 Go 语言(05):数据类型之数组与切片](https://studygolang.com/articles/27508) [从零学习 Go 语言(06):数据类型之字典与布尔类型](https://studygolang.com/articles/27563) [从零学习 Go 语言(07):数据类型之指针](https://studygolang.com/articles/27585) [从零学习 Go 语言(08):流程控制之if-else](https://studygolang.com/articles/27613) [从零学习 Go 语言(09):流程控制之switch-case](https://studygolang.com/articles/27660) [从零学习 Go 语言(10):流程控制之for 循环](https://studygolang.com/articles/28120) [从零学习 Go 语言(11):goto 无条件跳转](https://studygolang.com/articles/28472) [从零学习 Go 语言(12):流程控制之defer 延迟语句](https://studygolang.com/articles/28515) [从零学习 Go 语言(13):异常机制 panic 和 recover](https://studygolang.com/articles/28519) [从零学习 Go 语言(14):Go 语言中的类型断言是什么?](https://studygolang.com/articles/29305) [从零学习 Go 语言(15):学习 Go 语言的结构体与继承](https://studygolang.com/articles/29306) [从零学习 Go 语言(17):Go 语言中的 make 和 new 有什么区别?](https://studygolang.com/articles/29315) [从零学习 Go 语言(18):Go 语言中的 语句块与作用域](https://studygolang.com/articles/29365) [从零学习 Go 语言(19):Go Modules 前世今生及入门使用](https://studygolang.com/articles/29371) [从零学习 Go 语言(20):关于包导入必学的 8 个知识点](https://studygolang.com/articles/29404) [从零学习 Go 语言(21):一文了解 Go语言中编码规范](https://studygolang.com/articles/29477) [从零学习 Go 语言(22):Go 语言中如何开源自己写的包给别人用?](https://studygolang.com/articles/29609) [从零学习 Go 语言(23):一篇文章搞懂 Go 语言的函数](https://studygolang.com/articles/29628) [从零学习 Go 语言(24):理解 Go 语言中的 goroutine](https://studygolang.com/articles/29641) [从零学习 Go 语言(25):详解信道/通道](https://studygolang.com/articles/29704) [从零学习 Go 语言(26):通道死锁经典错误案例详解](https://studygolang.com/articles/29756) [从零学习 Go 语言(27):学习 Go 协程中的 WaitGroup](https://studygolang.com/articles/29783) [从零学习 Go 语言(28):学习 Go 协程中的互斥锁和读写锁](https://studygolang.com/articles/29838) [从零学习 Go 语言(29):Go 语言中的 select 用法](https://studygolang.com/articles/29852) [从零学习 Go 语言(30):如何使用 GDB 调试 Go 程序?](https://studygolang.com/articles/29867) [从零学习 Go 语言(31):Go 语言里的空接口](https://studygolang.com/articles/29885) [从零学习 Go 语言(32):理解 Go 语言中的 Context](https://studygolang.com/articles/29900) [从零学习 Go 语言(33):如何手动实现一个协程池?](https://studygolang.com/articles/29943) --- ![](http://image.python-online.cn/20200321153457.png)

有疑问加站长微信联系(非本文作者))

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1009 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传