从零开始学GO ---- 接口

2023-05-16

从零开始学GO ---- 接口

接口是一个编程规范,一组方法签名的集合。Go的接口是非侵入式的设置,一个具体类型实现接口不需要在语法上显式地声明,只要具体类型的方法集是接口方法集的超集,就代表该类型实现了接口,编译器在编译时进行方法的校验。接口定义了一个对象的行为规范,只定义规范不实现,由具体的对象来实现规范的细节。

在Go语言中接口(interface)是一种类型,一种抽象的类型。

接口声明

Go语言接口分为 接口字面变量类型和接口命名类型,用interface关键字声明

接口字面变量类型的声明语法:

interface{
	MethodSignature1
	MethodSignature2
}

接口命名类型使用type关键字声明:

type InterfaceName interface{
	MethodSignature1
	MethodSignature2
}

接口定义的大括号内是方法声明的集合,也可以嵌入另一个接口类型匿名字段。

例如:

type Reader interface{
    Read(p []byte) (n int,err error)
}
type Writer interface{
    Writer(p []byte)(n int,err error)
}

type ReadWriter interface{
    Reader
    Writer
}
//《====》等价于
type ReadWriter interface{
    Read(p []byte)(n int,err error)
    Writer(p []byte)(n int,err error)
}

声明新接口类型的特定:

  • 接口的命名一般以"er"结尾
  • 接口定义的内部方法不需要func引导
  • 在接口定义中,只有方法声明没有方法实现

接口初始化

一个对象只要全部实现了接口中的方法,那么就实现了这个接口。换句话说,接口就是一个需要实现的方法列表,因此实现了该接口中所有的方法即实现了接口的初始化。

接口初始化的例子:

// Sayer 接口
type Sayer interface {
	say()
}
//dog和cat接口体
type dog struct {}
type cat struct {}
//因为Sayer接口里只有一个say方法,所以只需要给dog和cat 分别实现say方法就可以实现Sayer接口了。
// dog实现了Sayer接口
func (d dog) say() {
	fmt.Println("汪汪汪")
}

// cat实现了Sayer接口
func (c cat) say() {
	fmt.Println("喵喵喵")
}

func main() {
	var x Sayer // 声明一个Sayer类型的变量x
	a := cat{}  // 实例化一个cat
	b := dog{}  // 实例化一个dog
    //由于cat和dog都实现了Sayer接口,因此Sayer接口变量可以被赋值为cat和dog
	x = a       // 将cat实例直接赋值给x
	x.say()     // 喵喵喵
	x = b       // 将dog实例直接赋值给x
	x.say()     // 汪汪汪
}

接口方法调用

接口方法调用的最终地址是在运行期决定的,将具体类型赋值给接口后,会使用具体类型的方法指针初始化接口变量,当调用接口变量的方法时,节间调用实例的方法。接口方法的调用不是一种直接调用,有一定的运行时开销。

直接调用未初始化的接口变量会产生 panic.

package main

type Printer interface {
	Print()
}

type S struct{}

func (s S) Print() {
	println("print")
}
func main() {
	var i Printer
	//未初始化的接口调用方法会产生panic
	//i.Print()
	//

	i = S{}   //进行初始化
	i.Print() //运行时简介调用S.Print
	//print
}

动态类型和静态类型

动态类型: 接口绑定的具体实例的类型称为接口的动态类型,动态类型随着不同的绑定会发生变化

静态类型: 接口被定义时,其类型就完全被确定,这个类型就是接口的静态类型,静态类型本质特征是接口方法签名集合,两个接口如果方法集合相同的话则静态类型一致可以相互赋值。如果a接口的方法集A,b接口的方法集B,其实B是A的子集合,则a的接口变量可以直接赋值给b的接口变量。

接口运算

接口类型断言

接口断言的语法形式:i.(TypeName) i是接口变量,TypeName可以是接口类型名,也可以是具体类型名

接口查询的两层语义:

  • 如果TypeName是一个具体类型名,则类型断言用来判断接口变量i绑定的实例类型是否就是具体类型TypeName
  • 如果TypeName是一个接口类型名,则类型断言可以判断接口变量i绑定的实例类型是否同时实现了TypeName接口

接口断言的两种语法表现:

直接赋值模式

o:=i.(TypeName)

  • 如果Typename是具体类型名,并且接口i绑定的实例类型就是具体类型TypeName,则变量o的类型就是TypeName,变量o的值是接口绑定的实例值的副本
  • 如果Typename是接口类型名,如果接口i绑定的实例类型满足接口类型TypeName,则变量o的类型就是TypeName,变量o底层绑定的具体类型实例就是i绑定的实例的副本
  • 否则,程序抛出panic
package main

import "fmt"

//实现一个内部接口
type Inter interface {
	Ping()
	Pang()
}

//实现一个外部接口
type Anter interface {
	Inter //内部接口
	String()
}

type St struct {
	Name string
}

//为 St实现Inter的接口方法集
func (St) Ping() {
	fmt.Println("Ping")
}

func (*St) Pang() {
	fmt.Println("Pang")
}

func main() {
	st := &St{"Tom"}
	var i interface{} = st //定义一个空接口绑定到st

	o := i.(Inter) //判断i绑定的实例是否实现了接口类型Inter
	o.Ping()       //Ping
	o.Ping()       //Pang

	//p := i.(Anter) //判断i绑定的实例是否实现了接口类型Anter
	//p.String()
	//panic: interface conversion: *main.St is not main.Anter: missing method String
	//因为i没有实现String故没有实现Anter,会引发panic

	s := i.(*St) //判断i绑定的实例是否就是具体类型St
	fmt.Printf("%s", s.Name)   //Tom
}

comma, ok 表达式模式:

if o,ok:=i.(TypeName);ok{ }

  • TypeName是具体类型名,接口i绑定的实例类型就是具体类型TypeName,则oktrue,变量o类型就是TypeName,变量o的值就是接口绑定的实例值的副本
  • TypeName是接口类型名,接口i绑定的实例类型满足接口类型TypeName,则oktrue,变量o类型就是接口类型TypeName,变量o底层绑定的具体类型是i绑定的实例的副本
  • 否则,okfalse,变量oTypeName类型的零值,此种条件下的分支逻辑不应该再去引用o
package main

import "fmt"

//实现一个内部接口
type Inter interface {
	Ping()
	Pang()
}

//实现一个外部接口
type Anter interface {
	Inter //内部接口
	String()
}

type St struct {
	Name string
}

//为 St实现Inter的接口方法集
func (St) Ping() {
	fmt.Println("Ping")
}

func (*St) Pang() {
	fmt.Println("Pang")
}

func main() {
	st := &St{"Tom"}
	var i interface{} = st //定义一个空接口绑定到st

	if o, ok := i.(Inter); ok { //判断i绑定的实例是否实现了接口类型Inter
		o.Ping() //Ping
		o.Pang() //Pang
	}

	if p, ok := i.(Anter); ok {
		//由于i没有实现接口Anter,故ok为false,程序不执行到这里
		p.String()
	}

	if s, ok := i.(*St); ok {
		fmt.Printf("%s", s.Name) //Tom
	}

}

接口类型查询

接口类型查询语法:

switch v:=i.(type){
case type1:
	xxxx
case type2:
    xxxx
default:
    xxxx
}
  • i是一个接口类型
  • case后可以接接口类型名,也可以接 非接口类型名
    • case 接口类型名,如果i绑定的实例类型实现了该接口类型的方法则匹配成功,v的类型是接口类型,v底层绑定的实例是i绑定具体类型实例的副本
    • case 具体类型名,如果接口变量i绑定的实例类型和该具体类型相同,则匹配成功,此时v就是该具体类型变量,v的值是i绑定的实例值的副本
    • 均不匹配的话执行default,v的值为0,没有意义
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

从零开始学GO ---- 接口 的相关文章

  • 将函数实现放在头文件中

    研究一个开源算法库 xff0c 采用C 43 43 模板编程 xff0c 所有函数实现都放在了头文件中 xff0c 现在把模板去掉 xff0c 链接时发生冲突 xff0c 具体原因如下 xff1a 因为多个源文件包含了含有函数定义的头文件
  • 编译器错误消息: CS1617: 选项“6”对 /langversion 无效

    编译错误 说明 在编译向该请求提供服务所需资源的过程中出现错误 请检查下列特定错误详细信息并适当地修改源代码 编译器错误消息 CS1617 选项 6 对 langversion 无效 xff1b 必须是 ISO 1 ISO 2 3 4 5
  • 无法序列化会话状态

    添加购物车功能遇到返回错误500 xff0c 解决办法 xff1a 因为ajax post请求后 xff0c 只返回错误500的信息 xff0c 并不能清楚的知道具体问题 xff0c 所以要在js处理返回错误500的方法中添加上xhr re

随机推荐

  • C#获取网络图片

    简单获取图片 string url 61 zhi txt Text 图片地址 string dizhi 61 lujing Text 图片下载后保存路径及图片名称要写在一块 WebClient wc 61 new WebClient wc
  • 在vscode中使用Git

    用了git最方便的就是比如在公司写了很多代码后回到家打开vscode只需要点击一下pull就能全部同步过来 是不是很方便 毕竟之前我都是拿u盘拷贝回家或者存到云盘再下载下来 我这里用的是国内的码云托管的代码 xff0c xff0c gith
  • vscode同步设置&扩展插件

    首先安装同步插件 xff1a Settings Sync 第二步 xff1a 进入你的github如图 xff1a 打开设置选项 xff1a 新建一个token xff1a 如图 xff1a 记住这个token值 转到vscode 按shi
  • mysql 常用语句使用

    1 查询语句 SELECT FROM table 2 更改语句 UPDATE table SET name 61 39 123456 39 WHERE id 61 100 3 插入语句 INSERT INTO table VALUES 1
  • STM32学习第七天--串口调试助手没弄懂

    啊啊 啊 今天真的好沮丧 调代码足足调了一晚上 xff0c 不知道什么原因工程就是错 xff0c 最后好不容易啊 xff0c 在主函数加了个 include 34 stm32f10x lib h 34 就好使了 xff0c 真不知道为什么
  • swoole 相关

    安装虚拟机 VMware Workstation Pro 安装CentOS CentOS 7 x86 64 Minimal 1708 iso 安装FinalShell 教程地址 安装lnmp 教程地址 服务状态管理命令 1 安装lnmp 2
  • ffmpeg编程:读取摄像头信息,保存为裸yuv420p、yuyv422视频流

    1 源码下载 xff1a https download csdn net download dijkstar 10898462 2 编程环境使用Windows下的QT5 11 minGW32 xff0c 源码中已经放好了fmpeg的bin
  • C中__FILE__ __LINE__的用法

    include lt stdio h gt void main void printf 34 File s Successfully reached line d n 34 FILE LINE Other statements here l
  • ubuntu中添加和删除源

    添加PPA源的命令为 xff1a br sudo add apt repository ppa user ppa name 添加好更新一下 xff1a sudo apt get update 删除命令格式则为 xff1a br sudo a
  • jetson nano 部署yoloV3,yoloV4,yoloV3-tiny,yoloV4-tiny

    系统 ubuntu nbsp nbsp 自带cuda10 0 nbsp 1 下载与安装darknet git clone https github com AlexeyAB darknet cd darknet 2 以下步骤我都在直接进入c
  • 对比first-fit/best-fit/worst-fit/slab以及buddy这几种算法的特点

    以下均为自己对这些算法的理解 xff1a fitst fit算法 First fit算法 xff1a 连续物理内存分配算法的一种 xff0c 将空闲内存块按照地址从小到大的方式连起来 xff0c 具体实现时使用了双向链表的方式 当分配内存时
  • MIT6_0001F16_Pset2

    MIT6 0001F16 Pset2 完成Hangman Game的编写 xff0c 就是一个猜词游戏 span class token comment Problem Set 2 hangman py span span class to
  • 利用python进行数据分析 pdf

    利用python进行数据分析 链接 xff1a https pan baidu com s 1mFg7kB0WG6edKnhumMbbJg 提取码 xff1a 6kos 如果带来帮助 xff0c 可点赞或关注博主 xff01
  • 数据库系统概念第六版 第六章练习题6.11

    6 11 考虑下图所示的关系数据库 xff0c 主码加了下划线 给出关系代数表达式来表达下列的每一个查询 xff1a a 找出First Bank Corporation的所有员工姓名 b 找出First Bank Corporation所
  • 数据库实验4 SQL语言-SELECT查询操作

    数据库实验4 SQL语言 SELECT查询操作 1 首先按照第三章的jxgl数据库的模板创建jxgl数据库并插入数据 xff1a 创建数据库jxgl create database jxgl 创建相应的表 xff1a 创建student表
  • Docker安装RabbitMQ,RabbitMQ Management使用

    上一篇文章介绍了RabbitMQ的一些基本概念 xff0c 如果没看过没了解 xff0c 推荐先去了解一下 xff1a 下面我们一起来安装一下RabbitMQ并且访问一下它的Management吧 我这边是使用了Docker容器来安装和启动
  • 数据库系统概念第六版 第七章练习题 15 22

    数据库第七章理论习题 7 15 为医院构造一个包含一组病人和一组医生的E R图 为每个病人关联一组不同的检查和化验记录 说明 xff1a 医生和病人间存在联系 xff0c 同时病人有特有的检查和化验记录 xff0c 病人有病床 xff0c
  • 操作系统设计思想--主奴思想

    操作系统设计思想 主奴思想 对于操作系统而言 xff0c 如何稳定可靠运行无疑是最重要的 目前的方案均为将用户进程和其他用户进程 将用户进程和操作系统进行分离 实现操作系统可以管理用户进程 xff0c 但用户进程不能侵入内核 xff0c 同
  • 从零开始学GO ---- 错误处理

    从零开始学GO 错误处理 Go 语言通过内置的错误接口提供了非常简单的错误处理机制 error类型接口 在这个接口类型的声明中只包含了一个方法Error Error方法不接受任何参数 xff0c 但是会返回一个string类型的结果 它的作
  • 从零开始学GO ---- 接口

    从零开始学GO 接口 接口是一个编程规范 xff0c 一组方法签名的集合 Go的接口是非侵入式的设置 xff0c 一个具体类型实现接口不需要在语法上显式地声明 xff0c 只要具体类型的方法集是接口方法集的超集 xff0c 就代表该类型实现