Go接口小结

1、所谓实现接口实际上就是实现接口内声明的所有的方法集,故而只要具体类型的方法集是接口方法集的超集,就代表该类型实现了接口,编译器在编译时会进行方法集的校验 。
2、 接口是没有具体实现辑的,也不能定义字段。
3、由于空接口的方法集为空,所以任意类型都被认为实现了 空接口(可根据1中定义理解) ,任意类型的实例都可以赋值或传递给空接口 ,包括非命名类型的实例。
4、非命名类型由于不 能定义自己的方法 所以方法集为空,因此其类型变量除了传
递给空接口 ,不能传递给任何其他接口(需要回头梳理一下Go的类型系统)。
5、声明新接口类型的特点

 (1)接口的命名一般以“er ”结尾
 (2)接口定义的内部方法声明不需要 func 引导。
 (3)在接口定义中,只有方法声明没有方法实现。

6、没有初始化的接口变量,其默认值是 nil。
7、接口初始化:即接口绑定具体类型的实例的过程称,有如下两种方法:

 (1)实例赋值接口:
 			要求:具体类型实例的方法集是某个接口的方法集的超集。
 			接口被初始化后,调用接口的方法就相当于调用接口绑定的具体类型的方法(类似于C++的多态特性)。
 (2)接口变 赋值接口变量:
 			要求:已经初始化的接口类型变量a直接赋值给另一种接口变量b,要求b的方法集是a的方法
的子集,此时接口变量b绑定的具体实例是接口变量a绑定的具体实例的副本。(结合实例赋值接口理解)

8、接口方法调用和普通的函数调用是有区别的。接口方法调用的最终地址是在运行期决定的将具体类型变量赋值给接口后,会使用具体类型的方法指针初始化接 口变 ,当调用接口变的方法时,实际上是间接地调用实例的方法。接口方法调用不是一种直接的调用,有一定的运行时开销。
9、接口的动态类型和静态类型

(1)动态类型:接口绑定的具体实例的类型称为接口的动态类型。接口可以绑定不同类型的实例,所以接口的动态类型是随着其绑定的不同类型实例而发生变化的。
(2)静态类型:接口被定义时, 其类型就已经被确定。,静态类型的本质特征就是接口的方法签名集合。两个接口如果方法签名集合
相同(方法的顺序可以不同),则这两个接口在语义上完全等价,它们之间不需要强制类型转换就可以相互赋值。原因是 Go 编译器校验接口是否能赋值,是比较二者的方法集,而不是看具体接口类型名。

10、类型断言

(1)语法形式:i . (TypeNname):i必须是接口变 ,如果是具体类型变量,则编译器会报 on interface type xxx on left, TypeNname 可以是接口类型名,也可以是具体类型名。
(2)接口查询的两层语义:
	i、如果 TypeNname 是一个具体类型名,则类型断言用于判断接口变量 绑定的实例类
型是否就是具体类型 TypeNname。
	ii、如果 TypeName 是一个接口类型名,则类型断言用于判断接口变量 绑定的实例类型
是否同时实现了 ypeName 接口。
也就是说:(o := i . (TypeName))
	TypeNam 是具体类型名,此时如果接口i绑定的实例类型就是具体类型 TypeName,则变量o的类型就是 TypeName 变量,o的值就是接口绑定的实例值的副本(当然实例可能是指针值,那就是指针值的副本)
	TypeName 是接口类型名,如果接口i绑定的实例类型满足接口类型 Type :Name ,则o的类型就是接口类型 TypeName 低层绑定的具体类型实例是i绑定的实例的副本(当然实例可能是指针值,那就是指针值的副本〉。
	如果上述两种情况都不满足, 程序抛 panic。
(3)comma,ok 表达式模式如下:if o , ok : = i. (TypeName);ok { ... },若i为TypeName类型,则ok为true。

11、类型查询

(1)语法格式如下:
			switch v :=i. (type) { 
			case typel : 
					xx xx 
			case type2 : 
					xx xx 
			default : 
					xx xx
(2)语义分析:
	接口查询有两层语义, 一是查询变量底层绑定的底层变量具体类型是什么,二是查接口变量绑定的底层变量是否还实现了其他接口。
	要求:
	(1)i必须是接口类型:因为具体类型实例的类型是静态的 在类型声明后就不再变化,所以具体类型的变量不存在类型查询,类型查询一定是对一个接口变量进行操作。
	(2)case 字句后面可以跟非接口类型名,也可以跟接口类型名,匹配是按照 case 子句的顺序进行的:
		如果 case 后面是一个接口类型名,且接口变量i绑定的实例类型实现了该接口类型的方,则匹配成 功,v的类型是接口类型, v底层绑定的实例是i绑定具体类型实例的副本。
		如果 case 后面是具体类型名,且接口变量i绑定的实例类型和该具体类型相同,则匹配成功,此时 v就是该具体类型变量, v的值是i绑定的实例值的副本。	
		如果 case 后面跟着多个类型,使用逗号分隔,接口变量i绑定的实例类型只要和其中一个类型匹配,则直接使用o赋值给v ,相当于 v:= o, 这个语法有点奇怪,按理说编译器不应该允许这种操作,语言实现者可能想让 type switch 语句和普通的 switch 语句保样的语法规则,允许发生这种情况。
		如果所有的 case 字句都不满足, 则执行 default 语句,此时执行的仍然是 v:= o,最终v的值是 o, 此时使用v没有任何意义。
		fall through 语句不能在 Type Switch 吾句中使用.

12、类型查询和类型断言

(1)类型查询和类型断言具有相同的语义,只是语法格式不同。二者都能判断接口变量绑定的实例的具体类型,以及判断接口变量绑定的实例是否满足另一个接口类型。
(2)类型查询使用 case 字句一次判断多个类型,类型断言一次只能判断一个类型,当然类型断言也可以使用if else if 语句达到同样的效果。

13、接口主要使用在如下地方:

(1)作为结构 嵌字段。
(2)作为函数或方法的形参。
(3)作为函数或方法的返回值。
(4)作为其他接口定义的嵌入宇段。

14、空接口的用途

(1)空接口和泛型:Go 语言没有泛型,如果一个函数需要接收任意类型的参数, 则参数类型可以是空接口,这是弥补没有泛型的一种手段。
(2)空接口和反射:空接口是反射实现的基础,反射库就是将相关具体的类型转换并赋值给空接口后才去处理。
(3)通过接口类型断言。
(4)通过接口类型查询。
(5)通过反射。

15、空接口有两个字段,一 个是实例类型, 一个是指向绑定实例的指针,只有两个都为 nil ,空接口才为 nil。
16、接口变量必须初始化才有意义,没有初始化的接口变量的默认值是 nil,没有任何意义。
17、接口调用代价

接口动态调用过程,这个过程有两部分多余时耗,一个是接口实例化的过程,也就是 iface 结构建立的过程, 一旦实例化后,这个接口和具体类型的 ita 数据结构是可以复用的;另一个是接口的方法调用,它是一个函数指针的间接调用。同时我门应考虑到接口调用是一种动态的计算后的跳转调用,这对现代的计算机 CPU 的执行很不友好,会导致 CPU 缓存失效和分支预测失败,这也有一部分的性能损失。但是,动态调用的性能损失几乎可以忽略不计。

18、接口底层对应的数据结构
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值