GO语言中的接口(interface)
go 接口 interface
- 1、什么是接口(interface)?
- 2、注意事项
- 3、interface底层实现
- 4、侵入式与非侵入式
- 5、接口的应用场景
- 空接口的应用场景
- 6、其他使用
1、什么是接口(interface)?
在Go语言中,接口(interface)是方法的集合,它允许我们定义一组方法但不实现它们,任何类型只要实现了这些方法,就被认为是实现了该接口。
接口更重要的作用在于多态实现,它允许程序以多态的方式处理不同类型的值。接口体现了程序设计的多态和高内聚、低耦合的思想。
package mainimport "fmt"// 定义接口
type Person interface {GetName() stringGetAge() int
}// 定义结构体
type Student struct {Name stringAge int
}// 实现接口
// 实现 GetName 方法
func (s Student) GetName() string {fmt.Println("name:", s.Name)return s.Name
}// 实现 GetAge 方法
func (s Student) GetAge() int {fmt.Println("age:", s.Age)return s.Age
}// 使用接口
func main() {var per Personvar stu Studentstu.Name = "xiaozhang"stu.Age = 24per = stuper.GetName() // name: xiaozhangper.GetAge() // age: 24
}
2、注意事项
(1)接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)。
(2)接口中所有的方法都没有方法体,即都是没有实现的方法。
(3)在Go中,一个自定义类型需要将某个接口的所有方法都实现,才能说这个自定义类型实现了该接口。
(4)一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型。
(5)只要是自定义数据类型就可以实现接口,不仅仅是结构体类型(structs),还包括类型别名(type aliases)、其他接口、自定义类型、变量等。
(6)一个自定义类型可以实现多个接口。
(7)interface 接口不能包含任何变量。
(8)一个接口可以继承多个别的接口,这时如果要实现这个接口必须实现它继承的所有接口的方法。在低版本的Go编辑器中,一个接口继承其他多个接口时,不允许继承的接口有相同的方法名。比如A接口继承B、C接口,B、C接口的方法名不能一样。高版本的Go编辑器没有相关问题。
(9)interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil。
var i interface{}
fmt.Println(i == nil) // 输出:true
(10)在Go中,接口的实现是非侵入式,隐式的,不需要显式声明“我实现了这个接口”。只要一个类型提供了接口中定义的所有方法的具体实现,它就自动成为该接口的一个实现者。
(11)空接口interface{}没有任何方法,是一个能装入任意数量、任意数据类型的数据容器,我们可以把任何一个变量赋给空接口类型。任意数据类型都能实现空接口,这就和 “空集能被任意集合包含” 一样,空接口能被任意数据类型实现。
3、interface底层实现
Go的interface源码在Golang源码的runtime目录中。Go的interface是由两种类型来实现的:iface和eface。runtime.iface表示非空接口类型,runtime.eface表示空接口类型interface{}。
iface是包含方法的interface,如:
type Person interface {GetName()
}
eface是不包含方法的interface,即空interface,如:
type Person interface {
}
//或者
var person interface{} = xxxx实体
iface的源代码是:
type iface struct {tab *itab // 表示值的具体类型的类型描述符data unsafe.Pointer // 指向值的指针(实际的数据)
}
itab是iface不同于eface的关键数据结构。其包含两部分:一部分是唯一确定包含该interface的具体结构类型,一部分是指向具体方法集的指针。
4、侵入式与非侵入式
GO语言的接口是非侵入式接口。
- 侵入式
侵入式接口的实现是显式声明的,必须显式的表明我要继承那个接口,必须通过特定的关键字(如Java中的implements)来声明实现关系。
优点:通过侵入代码与你的代码结合可以更好的利用侵入代码提供给的功能。
缺点:框架外代码就不能使用了,不利于代码复用。依赖太多重构代码太痛苦了。
- 非侵入式
非侵入式接口的实现是隐式声明的,不需要通过任何关键字声明类型与接口之间的实现关系。只要一个类型实现了接口的所有方法,那么这个类型就实现了这个接口。
优点:代码可复用,方便移植。非侵入式也体现了代码的设计原则:高内聚,低耦合。
缺点:无法复用框架提供的代码和功能。
侵入式接口存在的问题:
(1)侵入式接口把实现类与具体接口绑定起来了,强耦合;
(2)假如修改了接口方法,则实现类方法必须改动;
(3)假如类想再实现一个接口,类方法也必须进行改动;
(4)后续实现此接口的类,必须了解相关的接口;
Go语言非侵入式的方式很好地解决了这几个问题,只要实现了与接口相同的方法,就实现了这个接口。随着代码量的增加,根本不需要关心实现了哪些接口,不需要刻意去先定义接口再实现接口,在原有类里新增实现接口时,不需要更改类,做到低侵入式、低耦合开发。
go语言中非侵入式接口的影响:
1、go语言标准库不再需要绘制类库的继承树。
2、实现类的时候,只需要关心自己应该提供哪些方法,不用再纠结接口需要拆得多细才合理。
3、接口由使用方按自身需求来定义,使用方无需关心是否有其他模块定义过类似的接口。
5、接口的应用场景
Go接口的应用场景包括多态性、解耦、扩展性、代码复用、API设计、单元测试、插件系统、依赖注入。
多态性:接口使得代码可以更加灵活地处理不同类型的数据。通过接口,可以编写更加通用的代码,而无需关心具体的数据类型。
解耦:通过接口将代码模块解耦,降低模块之间的耦合度。不同模块只需要遵循同一个接口,即可实现模块间的无缝整合。这样,当一个模块的实现发生变化时,其他模块不需要做出相应的修改。
扩展性:通过接口,可以轻松地为现有的类型添加新的功能,只需实现相应的接口,而无需修改原有的代码。这种方式使得代码更容易扩展和维护。
代码复用:接口提供了一种将相似行为抽象出来并进行复用的方式,从而减少了代码的重复性。这样,可以更加高效地编写和维护代码。
API设计:通过定义接口,可以规范API的输入和输出,提高代码的可读性和可维护性。
单元测试:通过使用接口,可以轻松地替换被测试对象的实现,从而实现对被测代码的独立测试。
插件系统:通过定义一组接口,不同的插件可以实现这些接口,并在程序运行时动态加载和使用插件。
依赖注入:通过定义接口,可以将依赖对象的创建和管理交给外部容器,从而实现松耦合的代码结构。
- 类型转换。可将接口变量还原为原始类型,或用来判断是否实现了某个更具体的接口类型。
var s int
var x interface
x = s
y , ok := x.(int)
//将interface 转为int,ok可省略 但是省略以后转换失败会报错,
//true转换成功,false转换失败, 并采用默认值
- 类型判断。用switch语句在多种类型间做出推断匹配,这样空接口就有更多发挥空间。
var x interfaceswitch val.(type) {case nil: fmt.Println("nil") case string:fmt.Println("type is string, ", val)case bool:fmt.Println("type is bool, ", val)case int:fmt.Println("type is int, ", val)case float32, float64:fmt.Println("type is float, ", val)default: fmt.Println("unknown")
}
- 实现多态功能。根据对象的实际定义来实现不同的行为,从而实现多态行为。
// 多态
package mainimport "fmt"// Shape 接口定义了一个计算面积的方法
type Shape interface {Area() float64
}// Rectangle 结构体实现了 Shape 接口
type Rectangle struct {Width, Height float64
}
func (r Rectangle) Area() float64 {return r.Width * r.Height
}// Circle 结构体实现了 Shape 接口
type Circle struct {Radius float64
}
func (c Circle) Area() float64 {return math.Pi * c.Radius * c.Radius
}// DescribeShape 接受 Shape 接口类型的参数,输出图形的面积
func DescribeShape(s Shape) {fmt.Printf("Shape Area: %.2f\n", s.Area())
}func main() {r := Rectangle{Width: 3, Height: 4}c := Circle{Radius: 5}// 计算不同图形的面积DescribeShape(r) // Shape Area: 12.00DescribeShape(c) // Shape Area: 78.54
}
- 作为函数参数或返回值。不推荐作为函数的返回值,代码的维护、拓展与重构将会变得极为痛苦。
package mainimport "fmt"func GetType(val interface{}) {switch val.(type) {case nil: fmt.Println("nil") case string:fmt.Println("type is string, ", val)case bool:fmt.Println("type is bool, ", val)case int:fmt.Println("type is int, ", val)case float32, float64:fmt.Println("type is float, ", val)default: fmt.Println("unknown") }
}func main() {GetType(3) // type is int, 3GetType("interface") // type is string, interfaceGetType(3.01) // type is float, 3.01
}
空接口的应用场景
(1)用空接口可以让函数和方法接受任意类型、任意数量的函数参数,空接口切片还可以用于函数的可选参数。
(2)空接口还可以作为函数的返回值,但是极不推荐这样干,因为代码的维护、拓展与重构将会变得极为痛苦。
(3)空接口可以实现保存任意类型值的字典 (map)。
6、其他使用
- interface接口嵌套
// 接口嵌套
package mainimport "fmt"// 定义接口
type Person interface {GetName() stringGetAge() int
}// 接口嵌套
type Test interface {GetSex() stringPerson // 继承Person
}type Student struct {Name stringAge int
}type Teacher struct {Name stringAge intSex string
}// 实现 GetName 方法
func (s Student) GetName() string {fmt.Println("name:", s.Name)return s.Name
}// 实现 GetAge 方法
func (s Student) GetAge() int {fmt.Println("age:", s.Age)return s.Age
}// 实现 GetName 方法
func (t Teacher) GetName() string {fmt.Println("name:", t.Name)return t.Name
}// 实现 GetAge 方法
func (t Teacher) GetAge() int {fmt.Println("age:", t.Age)return t.Age
}// 实现 GetSex 方法
func (t Teacher) GetSex() string {fmt.Println("sex:", t.Sex)return t.Sex
}func main() {var per Personvar stu Studentvar tea Teacherstu.Name = "xiaozhang"stu.Age = 24tea.Name = "lilaoshi"tea.Age = 40tea.Sex = "man"per = stuper.GetName() // name: xiaozhangper.GetAge() // age: 24per = teaper.GetName() // name: lilaoshiper.GetAge() // age: 40var test Testtest = teatest.GetName() // name: lilaoshitest.GetAge() // age: 40test.GetSex() // sex: man
}
- interface 接口组合
// 接口的组合继承
package mainimport "fmt"// 可以闻
type Smellable interface {smell()
}
// 可以吃
type Eatable interface {eat()
}
// 接口组合
type Fruitable interface {SmellableEatable
}// 苹果既可能闻又能吃
type Apple struct{}func (a Apple) smell() {fmt.Println("apple can smell")
}func (a Apple) eat() {fmt.Println("apple can eat")
}// 花只可以闻
type Flower struct{}func (f Flower) smell() {fmt.Println("flower can smell")
}func main() {var s1 Smellablevar s2 Eatablevar apple = Apple{}var flower = Flower{}s1 = apples1.smell() // apple can smells1 = flowers1.smell() // flower can smells2 = apples2.eat() // apple can eatvar s3 Fruitables3 = apples3.smell() // apple can smells3.eat() // apple can eat
}
相关文章:
GO语言中的接口(interface)
go 接口 interface 1、什么是接口(interface)?2、注意事项3、interface底层实现4、侵入式与非侵入式5、接口的应用场景空接口的应用场景 6、其他使用 1、什么是接口(interface)? 在Go语言中,接口…...
模拟电路再理解系列(2)-电源滤波电路
前言 今天梳理一下电源滤波电路,在大部分电路设计中,都有滤波电路这一块儿,严格地说的话除了电源滤波还有信号滤波,经常会看到电路里面有一些电容,电感等串并联在一起的模块,大概率就是起到滤波作用&#…...

uniapp使用多列布局显示图片,一行两列
完整代码: <script setup>const src "https://qiniu-web-assets.dcloud.net.cn/unidoc/zh/shuijiao.jpg" </script><template><view class"content"><view class"img-list"><image :src"src…...

近期几首小诗汇总-生活~卷
生活 为生活飘零,风雨都不阻 路见盲人艰,为她心点灯 贺中科大家长论坛成立十五周年 科学家园有喜贺 园外丑汉翘望中 曾一学子入我科 正育科二盼长大 憧憬也能入此家 与科学家论短长 园外翘首听高论 发现有隙入此坛 竟然也能注册成 入园浏览惶然立 此贴…...

超时导致SparkContext构造失败的问题探究
文章目录 1.前言2. 基于事故现场对问题进行分析2.1 日志分析2.2 单独测试Topology代码试图重现问题 3. 源码解析3.1 Client模式和Cluster模式下客户端的提交和启动过程客户端提交时在两种模式下的处理逻辑ApplicationMaster启动时在两种模式下的处理逻辑 3.2 两种模式下的下层角…...

【人工智能】Transformers之Pipeline(一):音频分类(audio-classification)
目录 一、引言 二、音频分类(audio-classification) 2.1 概述 2.2 技术原理 2.2.1 Wav2vec 2.0模型 2.2.1 HuBERT模型 2.3 pipeline参数 2.3.1 pipeline对象实例化参数 2.3.2 pipeline对象使用参数 2.4 pipeline实战 2.4.1 …...
Nginx 负载均衡详解
Nginx是一个高性能的HTTP和反向代理服务器,拥有丰富的功能和模块,负载均衡就是其中之一。负载均衡是一种技术,用于在多台服务器之间分配工作负载,以确保高可用性和可靠性。本文将详细介绍Nginx的负载均衡算法、工作原理、配置方法…...
Unity3D开发之传送带实现
/// <summary> /// 传送带 直线传送带 /// </summary> public class ConveyerBelt : MonoBehaviour {public float Speed 1;protected float mspeed;protected Vector3 direction;protected Rigidbody rd;List<GameObject> Goods new List<GameObject&…...

【学习笔记】无人机(UAV)在3GPP系统中的增强支持(二)-支持高分辨率视频直播应用
引言 本文是3GPP TR 22.829 V17.1.0技术报告,专注于无人机(UAV)在3GPP系统中的增强支持。文章提出了多个无人机应用场景,分析了相应的能力要求,并建议了新的服务级别要求和关键性能指标(KPIs)。…...
python的私有属性和数据封装
1.私有属性 在 Python 中,私有属性是一种编程约定,用于表示某些属性在类的内部使用,不希望被外部直接访问或修改。 私有属性的命名通常以双下划线 __ 开头。例如,在类中定义一个私有属性可以像这样: class MyClass:de…...
一文学会鉴别“套壳”ChatGPT模型
一文学会鉴别“套壳”ChatGPT模型 随着ChatGPT等明星模型的诞生,市场上也开始出现一些“套壳”现象,即部分模型表面标榜原创或先进,实则在核心算法上与知名模型高度相似。作为技术探索者,如何拨开迷雾,识别这些“李鬼…...

Docker基本管理1
Docker 概述 Docker是一个开源的应用容器引擎,基于go语言开发并遵循了apache2.0协议开源。 Docker是在Linux容器里运行应用的开源工具,是一种轻量级的“虚拟机”。 Docker 的容器技术可以在一台主机上轻松为任何应用创建一个轻量级的、可移植的、自给自…...

python-28-零基础自学python-json存数据、读数据,及程序合并
学习内容:《python编程:从入门到实践》第二版 知识点: import json引入、 try-except-else return def函数、打开文件、 练习内容: 练习10-11:喜欢的数 编写一个程序,提示用户输入喜欢的数ÿ…...

Excel第30享:基于辅助列的条件求和
1、需求描述 如下图所示,现要统计2022年YTD(Year To Date:年初至今日)各个人员的“上班工时(a2)”。 下图为系统直接导出的工时数据明细样例。 2、解决思路 Step1:确定逻辑。“从日期中提取出…...
Java查看RSA密钥的ASN1结构
背景:服务端使用Java开发的,接口需要客户端传一个RSA公钥,手机端使用Flutter开发的,然后就选择使用 pointycastle 生成密钥,很不巧,dart版本不像Java一样有个可以直接获取编码过的公钥信息的方法࿱…...

友思特方案 | 低延迟GigE Vision解决方案:用于红外设备、医疗和工业级探测面板
导读 维持实时视频系统软硬件的长期成本效益,是该系统在医疗、工业等领域广泛应用的前提。友思特低延迟GigE Vision解决方案创新性地突破了这一难题,提供高带宽且高可靠性的端到端网络链接,有效降低了开发成本、复杂性和时间。 引言 虽然实…...
网络安全策略:优先防护而非溯源的重要性
面对网络攻击,企业往往面临一个关键决策点:是立即投入资源进行攻击溯源,还是优先加强自身的防御体系。尽管溯源分析有助于了解攻击者的手段和动机,但在大多数情况下,优先强化防护是更为明智的选择。本文将探讨为何在遭…...
ES6 Iterator 与 for...of 循环(五)
Iterator 特性: 统一的接口:无论是数组、字符串还是自定义对象,只要它们有默认的迭代器,就可以使用 for…of 循环进行遍历。可迭代对象:具有 [Symbol.iterator] 属性的对象被认为是可迭代的。[Symbol.iterator] 是一个…...

typora删除多余图片
import os import re import shutil from bs4 import BeautifulSoupimport warningswarnings.filterwarnings(ignore) # 定义正则表达式用于匹配.md文件中的图片引用语句 pattern re.compile(r!\[.*]\((.*)\))# 获取所有.md文件中的图片引用路径 references set() md_filepat…...
【ceph】ceph-mon重新选举的情况
本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》:python零基础入门学习 《python运维脚本》: python运维脚本实践 《shell》:shell学习 《terraform》持续更新中:terraform_Aws学习零基础入门到最佳实战 《k8…...

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式
一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明:假设每台服务器已…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...

Python:操作 Excel 折叠
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 Python 操作 Excel 系列 读取单元格数据按行写入设置行高和列宽自动调整行高和列宽水平…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...

Android 之 kotlin 语言学习笔记三(Kotlin-Java 互操作)
参考官方文档:https://developer.android.google.cn/kotlin/interop?hlzh-cn 一、Java(供 Kotlin 使用) 1、不得使用硬关键字 不要使用 Kotlin 的任何硬关键字作为方法的名称 或字段。允许使用 Kotlin 的软关键字、修饰符关键字和特殊标识…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...