## 1\. 静态类型


所谓的**静态类型**(即 static type),就是变量声明的时候的类型。


```

var age int   // int 是静态类型

var name string  // string 也是静态类型复制代码

```


>给大家推荐我自己的qun:850973621

把各种 python 的高效的使用技

巧用电子书的形式展示出来。有兴趣

的可以进群看一看视频教学和录播




它是你在编码时,肉眼可见的类型。


## 2\. 动态类型


所谓的 **动态类型**(即 concrete type,也叫具体类型)是 程序运行时系统才能看见的类型。


这是什么意思呢?


我们都知道 **空接口** 可以承接什么问题类型的值,什么 int 呀,string 呀,都可以接收。


比如下面这几行代码


```

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 对中记录着实际变量的值和类型。


比如下面这条语句


```

var age int = 25复制代码

```


我们声明了一个 int 类型变量,变量名叫 age ,其值为 25


[图片上传中...(image-4ee283-1610431673021-7)]


知道了接口的组成后,我们在定义一个变量时,除了使用常规的方法(可参考:[**02\. 学习五种变量创建的方法**](http://mp.weixin.qq.com/s?__biz=MzU1NzU1MTM2NA==&mid=2247483669&idx=2&sn=e70a1400c094e981f15b8da552bd8fbf&chksm=fc355b7ecb42d26824985163a3ef0c3567134975637c4efc42161751f54ab10343b485b36e23&scene=21#wechat_redirect))


也可以使用像下面这样的方式


```

package main


import "fmt"


func main() {

    age := (int)(25)

    //或者使用 age := (interface{})(25)


    fmt.Printf("type: %T, data: %v ", age, age)

}复制代码

```


输出如下


```

type: int, data: 25复制代码

```


## 4\. 接口细分


根据接口是否包含方法,可以将接口分为 `iface` 和 `eface`。


### iface


第一种:**iface**,表示带有一组方法的接口。


比如


```

type Phone interface {

   call()

}复制代码

```


`iface` 的具体结构可用如下一张图来表示


[图片上传中...(image-267bf5-1610431673021-6)]


iface 的源码如下:


```

// 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**,表示不带有方法的接口


比如


```

var i interface{} 复制代码

```


eface 的源码如下:


```

// src/runtime/runtime2.go

// 空接口

type eface struct {

    _type *_type

    data  unsafe.Pointer

}复制代码

```


[图片上传中...(image-9b49d0-1610431673021-5)]


## 5.理解动态类型


前两节,我们知道了什么是动态类型?如何让一个对象具有动态类型?


后两节,我们知道了接口分两种,它们的内部结构各是什么样的?


那最后一节,可以将前面四节的内容结合起来,看看在给一个空接口类型的变量赋值时,接口的内部结构会发生怎样的变化 。


### iface


先来看看 iface,有如下一段代码:


```

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,暂无动态类型。


[图片上传中...(image-67c6c4-1610431673021-4)]


最后一行代码:reader = tty,tty 是一个 `*os.File` 类型的实例,此时reader 对象的静态类型还是 io.Reader,而动态类型变成了 `*os.File`。


[图片上传中...(image-e86d49-1610431673021-3)]


### eface


再来看看 eface,有如下一段代码:


```

//不带函数的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。


[图片上传中...(image-871913-1610431673020-2)]


最后一行代码:empty = tty,tty 是一个 `*os.File` 类型的实例,此时 `_type` 变成了 `*os.File`。


[图片上传中...(image-ba3c97-1610431673020-1)]


## 6\. 反射的必要性


由于动态类型的存在,在一个函数中接收的参数的类型有可能无法预先知晓,此时我们就要对参数进行反射,然后根据不同的类型做不同的处理。




原文链接:https://juejin.cn/post/6844904191232589838