当前位置: 首页 > news >正文

Go基础篇:接口

目录

  • 前言✨
  • 一、什么是接口?
  • 二、空接口 interface{}
    • 1、eface的定义
    • 2、需要注意的问题
  • 三、非空接口
    • 1、iface的定义
    • 2、itab的定义
    • 3、itab缓存

前言✨

前段时间忙着春招面试,现在也算告一段落,找到一家比较心仪的公司实习,开始慢慢回归状态,这后面几章我会学习go1.19版本的语言特性或者机制:类型系统、接口、断言以及反射的内容,也算是补上之前没有深入底层的内容。

一、什么是接口?

Go语言中的接口(interface)是一种类型,它定义了一组方法的集合,但没有具体的实现。接口可以被任何类型实现,只要该类型实现了接口中定义的所有方法。这种设计方式使得Go语言具有很高的灵活性和可扩展性。

// 定义一个接口,包含一个String方法
type Stringer interface {String() string
}// 定义一个结构体,实现Stringer接口
type Person struct {Name stringAge  int
}func (p Person) String() string {return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}func TestInterface(t *testing.T) {// 定义一个接口变量,只能存储实现了Stringer接口的值var str Stringer// str = 42 // 编译错误,int类型没有实现Stringer接口// str = "hello" // 编译错误,string类型没有实现Stringer接口str = Person{"Bob", 30}fmt.Println(str) // Bob (30)
}

在这里插入图片描述

Go语言接口的特点是:

  • 接口是一种抽象的类型,它只定义了一组方法,而不指定具体的实现。
  • 接口是隐式实现的,也就是说,任何类型只要实现了接口的所有方法,就可以被认为是该接口的实例,而不需要显式地声明。
  • 接口可以组合,也就是说,一个接口可以包含另一个接口的所有方法,一个接口可以由多个接口的方法组成,从而实现接口的多态。

Go语言接口的优点是:

  • 接口可以提高代码的复用性和可维护性,因为它可以将不同类型的对象抽象为统一的接口,从而降低了代码之间的耦合度。
  • 接口可以提高代码的灵活性和扩展性,因为它可以支持多种实现方式,从而增加了代码的可变性和可选择性。
  • 接口可以提高代码的测试性和可测性,因为它可以方便地使用模拟对象或桩对象来替代真实对象,从而简化了单元测试和集成测试。

二、空接口 interface{}

Go语言中的接口是一种类型,它定义了一组方法的集合,接口可以分为空接口和非空接口。空接口(interface{})是一种特殊的接口类型,它没有任何方法,因此可以表示任何类型的值。非空接口则是指至少有一个方法的接口类型。

Jordan Oreilli 对空接口的一个很好的定义:

接口是两件事物:它是一组方法,但它也是一种类型。
空接口 interface{} 类型是没有方法的接口。由于 Go 语言没有 implements 关键字,所有类型都至少实现零个方法,并且接口是隐式实现的,所有类型都满足空接口。

在这里插入图片描述

1、eface的定义

在 Go 的泛型未敲定前,空接口和断言广泛用于实现泛型,空接口的底层数据结构是 eface,它是一个结构体,包含两个字段:_type和data。

eface结构体定义如下:

type eface struct {_type *_typedata  unsafe.Pointer
}

其中,_type是一个指向类型信息的指针,它包含了类型的名称、大小、对齐方式等信息。(不了解的请看上一节篇《Go基础篇:类型系统》),data是一个指向实际值的指针,它可以存储任何类型的值。

举个简单的例子:

type MyString string// 输出val的值和类型,这里只是稍微用到了反射reflect的TypeOf方法获取类型,后面我会详细讲的
func Print(val interface{}) {fmt.Println(val, reflect.TypeOf(val))
}func TestPrint(t *testing.T) {name := "linzy1"Print(name)var Myname MyString = "linzy2"Print(Myname)
}

在这里插入图片描述

例子中在空接口被赋值string类型和自定义类型时的值虽然相同,但是类型却不同,且都不是空接口 interface{} 类型。这是为什么呢?

当我们使用空接口来存储一个值时,Go语言会将该值的类型信息和实际值分别存储在eface结构体的_type和data字段中。在需要使用该值时,Go语言会根据_type字段中的类型信息,将data字段中的值转换为相应的类型,并进行相应的操作。

在这里插入图片描述

具体来说,当一个空接口变量被赋值为string类型的值时,Go语言会将该值的类型描述信息存储在该变量的_type字段中。这个类型描述信息包含了string类型的名称、大小、对齐方式等信息,可以通过反射包中的TypeOf()函数获取。

需要注意的是,由于空接口可以存储任何类型的值,因此在使用空接口时需要进行类型断言或类型转换,以确保程序的正确性。

2、需要注意的问题

空接口可以存储任何类型的值,但是 []interface{} 空接口切片类型不一样,[]interface{} 可以存储任何类型的值,但是不能直接赋值切片或者切片转换。

举个简单的例子:

func PrintAll(vals []interface{}) {fmt.Println("vals Type is:", reflect.TypeOf(vals))for _, val := range vals {fmt.Println(val, reflect.TypeOf(val))}
}func TestPrint(t *testing.T) {names := []string{"stanley", "david", "oscar"}// ./interface_test.go:144:11: cannot use names (variable of type []string) as type []interface{} in argument to PrintAll// PrintAll(names)ns := make([]interface{}, len(names))for i, name := range names {ns[i] = name}PrintAll(ns)ns = []interface{}{"linzy", 32, 1.68}PrintAll(ns)
}

在这里插入图片描述

在Go语言中,[]interface{}和[]string是两种不同的类型,它们之间不能直接赋值或转换。虽然[]interface{}可以存储任何类型的值,包括string类型,但是它本身并不是string类型的切片,因此不能直接赋值给[]string类型的变量。

最核心的点在于 slicetype 中的 elem 类型描述不同,所以他们是两种不同的类型。

type slicetype struct {typ  _typeelem *_type
}

三、非空接口

非空接口是指具有实际类型的接口,想要赋值给非空接口类型必须要实现该接口的所有方法,也就是说,它不是空接口。

1、iface的定义

在Go语言中,非空接口的底层数据结构是iface,它是一个结构体,定义如下:

type iface struct {tab  *itabdata unsafe.Pointer
}

其中,tab是一个指向方法表的指针,data是一个指向实际对象的指针。

2、itab的定义

itab是一个结构体,它定义了接口类型和实现类型之间的关系,包括接口类型的方法集合、实现类型的方法集合和方法表等信息。itab的定义如下:

type itab struct {inter *interfacetype_type *_typehash  uint32_     [4]bytefun   [1]uintptr
}

其中,inter是一个指向接口类型的指针,_type是一个指向动态类型的指针,hash是实现类型的哈希值,fun是一个指向方法表的指针数组,指向的是动态类型实现接口方法的地址

interfacetype和_type都是类型对象,它们包含了类型的名称、大小、对齐方式、方法集合等信息。uintptr是一个无符号整数类型,它可以存储指针类型的值。

itab结构体的大小是可变的,因为它的最后一个字段fun是一个指针数组,它的长度取决于实现类型的方法集合的大小。当实现类型的方法集合发生变化时,Go语言会重新生成一个新的itab结构体,并将其添加到缓存中以便后续可以复用。

举个简单的例子:

func TestReadWriter(t *testing.T) {// 定义io.ReadWriter接口类型的变量rw,需要实现Read和Write两个方法var rw io.ReadWriterf, _ := os.Open("linzy.txt")defer f.Close()rw = f// os.Stat函数获取文件的大小Stat, _ := f.Stat()tmp := make([]byte, Stat.Size())rw.Read(tmp)fmt.Println(string(tmp))
}

rw 赋值后
赋值的变化过程:

  • 当你将f \*os.File类型变量赋值给rw变量时,Go语言会生成一个新的itab结构体。它的inter字段指向io.ReadWriter接口类型的描述信息,_type字段指向*os.File类型的描述信息,fun字段指向一个方法表,其中包含了io.ReadWriter接口类型的需要的所有方法,拷贝的是方法地址。
  • Go语言会使用一个全局的哈希表来缓存itab结构体,这个itab结构体会被缓存起来以便后续可以复用。
  • 当你调用rw.Read(tmp)方法时,Go语言会根据rw变量对应的itab指针找到对应的方法表,并调用其中的Read方法。

总结:
当你将一个实现了接口类型的值赋给接口类型的变量时,Go语言会根据实际对象的类型和接口类型的方法集合生成一个新的itab结构体,并将其赋值给接口类型变量对应的itab指针。
这个itab结构体包含了接口类型的方法集合、实现类型的方法集合和方法表等信息,通过这些信息可以实现接口方法的调用。

3、itab缓存

一个非空接口类型和一个动态类型就可以确定一个itab的内容,容易出现重复的itab结构体。Go为了避免重复生成itab结构体的开销,并且可以减少内存的使用,提高程序的性能。
itab可复用

Go语言会使用一个全局的哈希表来缓存itab结构体,这个哈希表的键是一个由<接口类型, 动态类型>组成的key,值是对应的*itab结构体的指针。当我们创建一个非空接口变量时,Go语言会先在缓存中查找对应的itab结构体,如果找到了就直接使用,否则就动态生成一个新的itab结构体,并将其添加到缓存中以便后续可以复用。

在这里插入图片描述
但是这里说的哈希表跟go的map哈希表不相同,itab设计的哈希表结构更简单:

type itabTableType struct {size    uintptr             // length of entries array. Always a power of 2.count   uintptr             // current number of filled entries.entries [itabInitSize]*itab // really [size] large
}

需要一个itab时,会首先去itabTable里查找,计算哈希值时会用到接口类型(itab.inter)和动态类型(itab._type)的类型:

func itabHashFunc(inter *interfacetype, typ *_type) uintptr {// compiler has provided some good hash codes for us.return uintptr(inter.typ.hash ^ typ.hash)
}

如果能查询到对应的itab指针,就直接拿来使用。若没有就要再创建,然后添加到itabTable中。

相关文章:

Go基础篇:接口

目录 前言✨一、什么是接口&#xff1f;二、空接口 interface{}1、eface的定义2、需要注意的问题 三、非空接口1、iface的定义2、itab的定义3、itab缓存 前言✨ 前段时间忙着春招面试&#xff0c;现在也算告一段落&#xff0c;找到一家比较心仪的公司实习&#xff0c;开始慢慢回…...

边缘计算:数字时代的新战场

随着数字化时代的到来&#xff0c;云计算已经成为了各行各业不可或缺的技术支持。但是&#xff0c;由于云计算涉及到数据的传输和存储&#xff0c;对于网络带宽和延迟的要求也非常高&#xff0c;这使得云计算难以满足一些低延迟、高实时性要求的场景。在这种情况下&#xff0c;…...

PBDB Data Service:Fossil occurrences(化石产出记录)

Fossil occurrences&#xff08;化石产出记录&#xff09; 描述摘要1. [Single fossil occurrence&#xff08;单条化石产出记录&#xff09;](https://blog.csdn.net/whitedrogen/article/details/130519180)2. [List of fossil occurrences&#xff08;化石产出记录列表&…...

虾皮Shopee商品详情接口(item_get-根据ID取商品详情)代码封装

item_get-根据ID取商品详情接口 通过代码封装该接口可以拿到商品标题&#xff0c;商品价格&#xff0c;商品促销信息&#xff0c;商品优惠价&#xff0c;商品库存&#xff0c;sku属性&#xff0c;商品图片&#xff0c;desc图片&#xff0c;desc描述&#xff0c;sku图片&#xf…...

原生js手动实现一个多级树状菜单效果(高度可过渡变化) + 模拟el-menu组件实现(简单版)

文章目录 学习链接效果图代码要点 简单模拟el-menu实现TestTree.vueMenu.vueSubMenu.vue 学习链接 vue实现折叠展开收缩动画 - 自己的链接 elment-ui/plus不定高度容器收缩折叠动画组件 - 自己的链接 vue的过渡与动画理解 Vue transition 折叠类动画自动获取隐藏层高度以及…...

RK3568平台开发系列讲解(Linux内存篇)Linux内存管理框架

🚀返回专栏总目录 文章目录 一、内核态内存分配二、用户态内存分配三、内存篇章更新哪些内容沉淀、分享、成长,让自己和他人都能有所收获!😄 📢本篇我们一起将整个内存管理的体系串起来。 对于内存的分配需求,可能来自内核态,也可能来自用户态。 一、内核态内存分配…...

你的编程能力从什么时候开始突飞猛进?

关于编程能力突飞猛进的原因和如何突破自己&#xff0c;以下是我的建议。 在过去的几年中&#xff0c;编程领域发生了很多变化。新的语言和技术不断涌现&#xff0c;使得程序员们需要不断学习和提高。作为一名程序员&#xff0c;编程能力的提高是非常重要的&#xff0c;有助于…...

滨州高企认定条件

认定为高新技术企业必须同时满足以下条件&#xff1a; (一)企业在申请认定时需要注册一年以上。 (二)公司通过自主开发、转让、赠与、并购等方式&#xff0c;获得对其主要产品(服务)在技术上发挥核心支持作用的知识产权所有权。 &#xff08;三&#xff09;对企业主要产品(服…...

Azkaban学习——单机版安装与部署

目录 1.解压改名 2.修改装有mysql的虚拟机的my.cnf文件 3.重启装有mysql的虚拟机 4.Datagrip创建azkaban数据库&#xff0c;执行脚本文件 5.修改/opt/soft/azkaban-exec/conf/azkaban.properties文件 6.修改commonprivate.properties 7.传入mysql-connector-java-8.0.29…...

table标签-移动端适配

封装一个组件&#xff0c;该组件需要根据不同设备屏幕宽度自适应调整展示方式。对于 PC 端&#xff0c;以类似 el-table 的形式展示数据&#xff0c;而移动端则以一个类似 item 的形式展示每行数据。 可以先在组件中判断设备类型&#xff0c;如以下示例代码所示&#xff1a; …...

Yolov8改进---注意力机制:DoubleAttention、SKAttention,SENet进阶版本

目录 🏆🏆🏆🏆🏆🏆Yolov8魔术师🏆🏆🏆🏆🏆🏆 1. DoubleAttention 2. SKAttention 3.总结...

【逆向工程核心原理:TLS回调函数】

TLS 代码逆向分析领域中&#xff0c;TLS&#xff08;Thread Local Storage&#xff0c;线程局部存储&#xff09;回调函数&#xff08;Callback Function&#xff09;常用反调试。TLS回调函数的调用运行要先于EP代码的执行&#xff0c;该特征使它可以作为一种反调试技术的使用…...

“Shell“Awk命令

文章目录 一.Awk二.Awk按行输出文本三.Awk按字段输出文本四.通过管道&#xff0c;双引号调用shell命令五.总结&#xff1a; 一.Awk Awk的工作原理&#xff1a; 逐行读取文本&#xff0c;默认以空格或tab键为分隔符进行分隔&#xff0c;将分隔所得的各个字段保存到内建变量中&a…...

射频放大器的原理和作用(射频放大器和功率放大器的区别)

射频放大器是一种电子电路&#xff0c;用于将输入信号增强到足够高的电平以驱动射频输出负载。其原理和作用如下&#xff1a; 射频放大器的工作原理是利用晶体管的三极管效应&#xff0c;将输入信号放大到足够的电平以驱动输出负载。在射频放大器中&#xff0c;输入信号经过输入…...

揭秘KubeEdge边缘网络项目EdgeMesh:如何打造高速、安全、低延迟的互联网连接

KubeEdge是由百度主导的边缘计算项目&#xff0c;旨在为物联网设备提供一种高效、安全的互联网连接方式。EdgeMesh是KubeEdge的核心组件之一&#xff0c;它是一种基于OpenDaylight的边缘网络协议&#xff0c;能够在物联网设备之间提供高速、可靠的互联网连接。 EdgeMesh的设计目…...

Java设计模式 14-访问者模式

访问者模式 这个模式用的很少&#xff0c;《设计模式》的作者评价为&#xff1a; 大多情况下&#xff0c;你不需要使用访问者模式&#xff0c;但是一旦需要使用它时&#xff0c;那就真的需要使用了 一、测评系统的需求 1)将观众分为男人和女人&#xff0c;对歌手进行测评&…...

【数据结构】线性表之链表

目录 前言一、链表的定义二、链表的分类1. 单向和双向2. 带头和不带头3. 循环和不循环4. 常用&#xff08;无头单向非循环链表和带头双向循环链表&#xff09; 三、无头单向非循环链表的接口及实现1. 单链表的接口2. 接口的实现 四、带头双向循环链表接口的及实现1. 双向链表的…...

微服架构基础设施环境平台搭建 -(四)在Kubernetes集群基础上搭建Kubesphere平台

微服架构基础设施环境平台搭建 -&#xff08;四&#xff09;在Kubernetes集群基础上搭建Kubesphere平台 通过采用微服相关架构构建一套以KubernetesDocker为自动化运维基础平台&#xff0c;以微服务为服务中心&#xff0c;在此基础之上构建业务中台&#xff0c;并通过Jekins自动…...

Linux开发板安装Python环境

1. 环境介绍 硬件&#xff1a;STM32MP157&#xff0c;使用的是野火出的开发板。 软件&#xff1a;Debian ARM 架构制作的 Linux 发行版&#xff0c;版本信息如下&#xff1a; Linux发行版本&#xff1a;Debian GNU/Linux 10 内核版本&#xff1a;4.19.94 2. Python 简介…...

ChatGPT 聊天接口API 使用

一、准备工作 1.准备 OPENAI_ACCESS_TOKEN 2.准备好PostMan 软件 二、测试交流Demo 本次使用POSTMAN工具进行快速测试&#xff0c;旨在通过ChatGPT API实现有效的上下文流。在测试过程中&#xff0c;我们发现了三个问题&#xff1a;    1.如果您想要进行具有上下文的交流&…...

label-studio的使用教程(导入本地路径)

文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法

深入浅出&#xff1a;JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中&#xff0c;随机数的生成看似简单&#xff0c;却隐藏着许多玄机。无论是生成密码、加密密钥&#xff0c;还是创建安全令牌&#xff0c;随机数的质量直接关系到系统的安全性。Jav…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

[ICLR 2022]How Much Can CLIP Benefit Vision-and-Language Tasks?

论文网址&#xff1a;pdf 英文是纯手打的&#xff01;论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误&#xff0c;若有发现欢迎评论指正&#xff01;文章偏向于笔记&#xff0c;谨慎食用 目录 1. 心得 2. 论文逐段精读 2.1. Abstract 2…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

鱼香ros docker配置镜像报错:https://registry-1.docker.io/v2/

使用鱼香ros一件安装docker时的https://registry-1.docker.io/v2/问题 一键安装指令 wget http://fishros.com/install -O fishros && . fishros出现问题&#xff1a;docker pull 失败 网络不同&#xff0c;需要使用镜像源 按照如下步骤操作 sudo vi /etc/docker/dae…...

css3笔记 (1) 自用

outline: none 用于移除元素获得焦点时默认的轮廓线 broder:0 用于移除边框 font-size&#xff1a;0 用于设置字体不显示 list-style: none 消除<li> 标签默认样式 margin: xx auto 版心居中 width:100% 通栏 vertical-align 作用于行内元素 / 表格单元格&#xff…...

网站指纹识别

网站指纹识别 网站的最基本组成&#xff1a;服务器&#xff08;操作系统&#xff09;、中间件&#xff08;web容器&#xff09;、脚本语言、数据厍 为什么要了解这些&#xff1f;举个例子&#xff1a;发现了一个文件读取漏洞&#xff0c;我们需要读/etc/passwd&#xff0c;如…...