持续更新 Go 语言学习进度中 ......
- GO语言学习笔记-类型篇 Study for Go! Chapter one - Type - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-表达式篇 Study for Go ! Chapter two - Expression - slowlydance2me - 博客园 (cnblogs.com)
- GO语言学习笔记-函数篇 Study for Go ! Chapter three - Function - slowlydance2me - 博客园 (cnblogs.com)
Study for Go ! Chapter six - Interface
1. Initialization
-
接口代表一种调用契约,是多个方法声明的集合
-
在某些动态语言里,接口 (interface)也被称作协议 (protocol)。准备交互的双方,共同遵循实现约定的规则,使得在无须知道对方身份的情况下进行协作。
-
接口要实现的是做什么,而不关心怎么做
-
接口解除了类型依赖,有助于减少用户可视方法,屏蔽内部结构和实现细节
-
但并不可以滥用接口,毕竟接口实现机制会有运行期开销
-
对于相同包,或者不会频繁变化的内部模块之间,并不需要抽象出接口来强行分离
-
接口最常用的使用场景是对包外提供访问,或预留扩展空间
-
golang 接口实现机制很简洁,只要目标类型方法集内包含接口声明的全部方法,就被视为实现了该接口,无须做显式声明,当然,目标类型可实现多个接口 (即 先实现类型,然后再抽象出所需接口,这种非侵入式设计有很多好处,例如:在项目前期就设计出最合理的接口并不容易,而在代码重构,模块分析时再分离出接口,用以解耦就很常见。另外在使用第三方库时,抽象出所需接口,即可屏蔽太多不需要关注的内容,也便于日后替换)
-
从内部实现来看,接口自身也是一种结构类型,只是编译器会对其做出很多限制
-
不能有字段
-
不能定义自己的方法
-
只能声明方法不能实现
-
可嵌入其他接口类型
-
-
接口通常以 er 作为名称后缀,方法名是声明组成成分,但参数名可不同或忽略
-
编译器通过方法集来判断是否实现了接口
-
如果接口没有任何方法声明,那么就是一个空接口 ( interface{} ),它的用途类似面向对象里的根类型 Object,可被赋值为任何类型的对象
-
接口变量默认值是 nil。如果实现接口的类型支持,可做相等运算
-
接口可像匿名字段那样,嵌入其他接口,目标类型方法集中必须有包含嵌入接口方法在内的全部方法才算实现了该接口
-
嵌入其他接口类型,相当于将其声明的方法集导入,这就要求不能有同名方法,因为不支持重载。
-
不能嵌入自身或循环嵌入,那会导致递归错误
-
超集接口变量可隐式转换为子集,反过来则不行
-
支持匿名接口类型,可用于变量定义,或作为结构字段类型
2. 执行机制
-
接口使用一个名为 itab 的结构存储运行期所需的相关类型信息
-
利用调试器,我们可以查看这些结构存储的具体内容
-
相关类型信息里保存了接口和实际对象的元数据,同时, itab 还用 fun 数组 (不定长结构)保存了实际方法地址,从而实现在运行期对目标方法的动态调用
-
在将对象赋值给接口变量时,会复制该对象
-
无法修改接口存储的复制品,因为它是 unaddressable 的
-
即使将其复制出来,用本地变量修改后,依然无法对 iface.data赋值,解决办法是将对象指针赋值给接口,那么接口内存储的就是指针的复制品
-
只有当接口变量内部的两个指针 ( itab 、data )都为 nil 时,接口才为 nil
3. 类型转换
-
类型推断可将接口变量还原为原始类型,或用来判断是否实现了某个更具体的接口类型
-
使用 ok-idiom 模式,即使转换失败也不会引发 panic ,还可用switch 语句在多种类型间做出判断,这样空接口就有更多发挥空间
-
type switch 不支持 fallthrought
4. 技巧
-
让编译器检查,确保类型实现了指定接口
-
定义函数类型,让相同签名的函数自动实现某个接口