go-101接口
在编译时刻,Go编译器将构建一个全局表用来存储代码中要用到的各个类型的信息。 对于一个类型来说,这些信息包括:此类型的种类(kind)
、此类型的所有方法和字段信息、此类型的尺寸,等等。 这个全局表将在程序启动的时候被加载到内存中。
在运行时刻,当一个非接口值被包裹到一个接口值,Go运行时(至少对于官方标准运行时来说)将分析和构建这两个值的类型的实现关系信息,并将此实现关系信息存入到此接口值内。 对每一对这样的类型,它们的实现关系信息将仅被最多构建一次。并且为了程序效率考虑,此实现关系信息将被缓存在内存中的一个全局映射中,以备后用。 所以此全局映射中的条目数永不减少。 事实上,一个非零接口值在内部只是使用一个指针字段来引用着此全局映射中的一个实现关系信息条目。
对于一个非接口类型和接口类型对,它们的实现关系信息包括两部分的内容:
- 动态类型(即此非接口类型)的信息。
- 一个方法表(切片类型),其中存储了所有此接口类型指定的并且为此非接口类型(动态类型)声明的方法。
这两部分的内容对于实现Go中的两个特性起着至关重要的作用。
- 动态类型信息是实现
反射
的关键。 - 方法表是实现多态(见下一节)的关键。
多态
多态是接口的一个关键功能和Go语言的一个重要特性。
当非接口类型T的一个值t被包裹在接口类型I的一个接口值i中,通过i调用接口类型I指定的一个方法时,事实上为非接口类型T声明的对应方法将通过非接口值t被调用。 换句话说,调用一个接口值的方法实际上将调用此接口值的动态值的对应方法
。 比如,当方法i.m被调用时,其实被调用的是方法t.m。 一个接口值可以通过包裹不同动态类型的动态值来表现出各种不同的行为
,这称为多态。
当方法i.m被调用时,i存储的实现关系信息的方法表中的方法t.m将被找到并被调用。 此方法表是一个切片,所以此寻找过程只不过是一个切片元素访问操作,不会消耗很多时间。
注意,在nil接口值上调用方法将产生一个恐慌
,因为没有具体的方法可被调用。
反射
一个接口值中存储的动态类型信息可以被用来检视此接口值的动态值和操纵此动态值所引用的值。 这称为反射。
即根据动态类型信息,可以查看动态值并对其进行操作。
本文下面将只介绍Go中的内置反射机制。在Go中,内置反射机制包括类型断言(type assertion)
和type-switch流程控制代码块
。
与接口相关的转换有四种:
(1) 非接口 -> 接口,要求必须实现此接口的方法
(2) 接口 -> 接口,要求同上相同
(3) 接口 -> 非接口,要求非接口必须实现此接口
(4) 接口 -> 接口,前者接口可能不符合后者接口,但它的动态类型符合
前两种情况都是在编译期验证的。
后两种是在运行时通过类型断言验证的。
具体的断言规则查看上一篇博文所讲。
type-switch语句
type-switch语句是go中最古怪的语法,可以看作类型断言的增强版。
与switch-case语句有些相似。格式如下:
switch aSimpleStatement; v := x.(type) {
case TypeA:
...
case TypeB, TypeC:
...
case nil:
...
default:
...
}
注意点:1. aSimpleStatement可省略 2. 不关心断言后的值时可省略v 3. case后跟多个类型、或为nil、或为default时,在此case词法块内v变量类型为接口类型,其值为接口值x的一个复制。
与switch-case的相同点:1. 最多只有一个default分支 2. default分支位置可以随意放置,可以是第一个、最后一个或者中间某一个 3. 可以不包含任何分支,被视为空操作; 不同点:falltrough语句不能出现在type-switch中。
接口内嵌
接口值比较
- 接口与非接口: 要求非接口(也就是具体类型)必须实现此接口,则具体类型被隐式转换为接口类型
- 两个接口值比较:只有两种情况下
==
结果才为true:1. 都为nil 2. 两者的动态类型相同,且动态值可比较且相等。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术