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

Go语言基础之接口

接口的定义

接口是一种由程序员来定义的类型,一个接口类型就是一组方法的集合,它规定了需要实现的所有方法。接口可以类比c++中的抽象类,接口中的方法可以类比为c++当中的纯虚函数

每个接口类型由任意个方法签名组成,接口的定义格式如下:

type 接口类型名 interface{方法名1( 参数列表1 ) 返回值列表1方法名2( 参数列表2 ) 返回值列表2…
}

其中:

  • 接口类型名:Go语言的接口在命名时,一般会在单词后面添加er,如有写操作的接口叫Writer,有关闭操作的接口叫closer等。接口名最好要能突出该接口的类型含义。
  • 方法名:当方法名首字母是大写且这个接口类型名首字母也是大写时,这个方法可以被接口所在的包(package)之外的代码访问。
  • 参数列表、返回值列表:参数列表和返回值列表中的参数变量名可以省略。

举个例子,定义一个包含Write方法的Writer接口。

type Writer interface{Write([]byte) error
}

当你看到一个Writer接口类型的值时,你不知道它是什么,唯一知道的就是可以通过调用它的Write方法来做一些事情。

实现接口的条件

接口就是规定了一个需要实现的方法列表,在 Go 语言中一个类型只要实现了接口中规定的所有方法,那么我们就称它实现了这个接口。

我们定义的Singer接口类型,它包含一个Sing方法。

// Singer 接口
type Singer interface {Sing()
}

我们有一个Bird 结构体类型如下。

type Bird struct {}

因为Singer接口只包含一个Sing方法,所以只需要给Bird结构体添加一个Sing方法就可以满足Singer接口的要求。

// Sing Bird类型的Sing方法
func (b Bird) Sing() {fmt.Println("汪汪汪")
}

这样就称为Bird实现了Singer接口。

为什么要使用接口

接口的使用场景类似于c++中的多态,都是实现一种定义多种实现的手段和方式

假设我们的代码世界里有很多小动物,下面的代码片段定义了猫和狗,它们饿了都会叫。

package mainimport "fmt"type Cat struct{}func (c Cat) Say() {fmt.Println("喵喵喵~")
}type Dog struct{}func (d Dog) Say() {fmt.Println("汪汪汪~")
}func main() {c := Cat{}c.Say()d := Dog{}d.Say()
}

这个时候又跑来了一只羊,羊饿了也会发出叫声。

type Sheep struct{}func (s Sheep) Say() {fmt.Println("咩咩咩~")
}

可以观察到,这组函数的函数签名是一样的,唯一不同的是他们的具体实现,据此,我们就可以把他们抽象出来

我们接下来定义一个饿肚子的场景。

// MakeCatHungry 猫饿了会喵喵喵~
func MakeCatHungry(c Cat) {c.Say()
}// MakeSheepHungry 羊饿了会咩咩咩~
func MakeSheepHungry(s Sheep) {s.Say()
}

接下来会有越来越多的小动物跑过来,我们的代码世界该怎么拓展呢?

在饿肚子这个场景下,我们可不可以把所有动物都当成一个“会叫的类型”来处理呢?当然可以!使用接口类型就可以实现这个目标。 我们的代码其实并不关心究竟是什么动物在叫,我们只是在代码中调用它的Say()方法,这就足够了。

我们可以约定一个Sayer类型,它必须实现一个Say()方法,只要饿肚子了,我们就调用Say()方法。

type Sayer interface {Say()
}

然后我们定义一个通用的MakeHungry函数,接收Sayer类型的参数。

// MakeHungry 饿肚子了...
func MakeHungry(s Sayer) {s.Say()
}

我们通过使用接口类型,把所有会叫的动物当成Sayer类型来处理,只要实现了Say()方法都能当成Sayer类型的变量来处理。

var c cat
MakeHungry(c)
var d dog
MakeHungry(d)

那实现了接口又有什么用呢?一个接口类型的变量能够存储所有实现了该接口的类型变量。

例如在上面的示例中,DogCat类型均实现了Sayer接口,此时一个Sayer类型的变量就能够接收CatDog类型的变量。

var x Sayer // 声明一个Sayer类型的变量x
a := Cat{}  // 声明一个Cat类型变量a
b := Dog{}  // 声明一个Dog类型变量b
x = a       // 可以把Cat类型变量直接赋值给x
x.Say()     // 喵喵喵
x = b       // 可以把Dog类型变量直接赋值给x
x.Say()     // 汪汪汪

空接口

空接口是指没有定义任何方法的接口类型。因此任何类型都可以视为实现了空接口。也正是因为空接口类型的这个特性,空接口类型的变量可以存储任意类型的值

package mainimport "fmt"// 空接口// Any 不包含任何方法的空接口类型
type Any interface{}// Dog 狗结构体
type Dog struct{}func main() {var x Anyx = "你好" // 字符串型fmt.Printf("type:%T value:%v\n", x, x)x = 100 // int型fmt.Printf("type:%T value:%v\n", x, x)x = true // 布尔型fmt.Printf("type:%T value:%v\n", x, x)x = Dog{} // 结构体类型fmt.Printf("type:%T value:%v\n", x, x)
}

空接口的应用

空接口作为函数的参数

使用空接口实现可以接收任意类型的函数参数。

// 空接口作为函数参数
func show(a interface{}) {fmt.Printf("type:%T value:%v\n", a, a)
}

空接口作为map的值

使用空接口实现可以保存任意值的字典。

// 空接口作为map值var studentInfo = make(map[string]interface{})studentInfo["name"] = "沙河娜扎"studentInfo["age"] = 18studentInfo["married"] = falsefmt.Println(studentInfo)

类型断言

接口值可能赋值为任意类型的值,那我们如何从接口值获取其存储的具体数据呢?

想要从接口值中获取到对应的实际值需要使用类型断言,其语法格式如下。

x.(T)

其中:

  • x:表示接口类型的变量
  • T:表示断言x可能是的类型。

该语法返回两个参数,第一个参数是x转化为T类型后的变量,第二个值是一个布尔值,若为true则表示断言成功,为false则表示断言失败。

举个例子:

func funcName(a interface{}) string {// 判断当前接收到的参数变量的类型是否是stringvalue, ok := a.(string)if !ok {fmt.Println("Type assertion failed")return ""}fmt.Println("Type assertion successful, the value is: ", value)return value
}

如果对一个接口值有多个实际类型需要判断,推荐使用switch语句来实现。

// justifyType 对传入的空接口类型变量x进行类型断言
func justifyType(x interface{}) {switch v := x.(type) {case string:fmt.Printf("x is a string,value is %v\n", v)case int:fmt.Printf("x is a int is %v\n", v)case bool:fmt.Printf("x is a bool is %v\n", v)default:fmt.Println("unsupport type!")}
}


文章转载自:
http://amyotrophia.hnsdj.cn
http://antideuteron.hnsdj.cn
http://cervical.hnsdj.cn
http://asosan.hnsdj.cn
http://bandoeng.hnsdj.cn
http://asepticize.hnsdj.cn
http://buckish.hnsdj.cn
http://ballonet.hnsdj.cn
http://centra.hnsdj.cn
http://bora.hnsdj.cn
http://besprinkle.hnsdj.cn
http://birthrate.hnsdj.cn
http://blockbuster.hnsdj.cn
http://admire.hnsdj.cn
http://amaranthine.hnsdj.cn
http://chromatoscope.hnsdj.cn
http://apogeotropically.hnsdj.cn
http://caginess.hnsdj.cn
http://chemiluminescence.hnsdj.cn
http://carotid.hnsdj.cn
http://burrow.hnsdj.cn
http://ammonia.hnsdj.cn
http://ceasefire.hnsdj.cn
http://callboard.hnsdj.cn
http://beadswoman.hnsdj.cn
http://battels.hnsdj.cn
http://babassu.hnsdj.cn
http://bewitching.hnsdj.cn
http://chloritic.hnsdj.cn
http://bonhomie.hnsdj.cn
http://www.dtcms.com/a/267081.html

相关文章:

  • CppCon 2018 学习:Sane and Safe C++ Class Types
  • FLAN-T5:规模化指令微调的语言模型
  • NumPy 函数库在数学建模中的基本使用方法
  • 电脑休眠控制工具,灵活设置防休眠
  • 通过 Windows 共享文件夹 + 手机访问(SMB协议)如何实现
  • Python(28)Python循环语句指南:从语法糖到CPython字节码的底层探秘
  • Everything 1.5.0.1393a高效实用的系统文件搜索工具(2025年7月4日更新)
  • 构建未来交互体验:AG-UI 如何赋能智能体与前端通信?
  • [论文阅读] 软件工程 | 可持续性标志在问答平台中的应用
  • AI语音训练——GPT-SoVITS(GSV)
  • Vue 笔记:动态绑定内联样式 :style 的关键语法注意事项
  • Spring Boot 框架创建一个简单的后端接口,并介绍如何使用 Apifox 连接该接口
  • Spring中实现依赖注入(DI)的三种方式
  • Spring Bean 生命周期 SmartLifecycle接口介绍和使用场景 和 Lifecycle对比
  • 【Linux】02_CentOS 7 开机运行级别详解:从基础概念到实战配置
  • 浅谈 Vue2 的 Mixin 混入和 Vue3 的 Hooks(组合式 API)
  • Eureka、Nacos、LoadBalance、OpenFeign​之间的区别联系和协作 (附代码讲解)
  • 深入理解Qt的SetWindowsFlags函数
  • Kafka消费者分区分配机制与生产环境配置指南
  • LabVIEW 3D 场景中 Voronoi 图(基站覆盖模拟)功能
  • 03每日简报20250705
  • 国产MCU学习Day7——CW32F030C8T6 SPI主从通信详解
  • Django中关于templates目录和static目录存放位置的总结
  • 基于开源AI智能名片链动2+1模式的S2B2C商城小程序:门店私域流量与视频号直播融合的生态创新研究
  • 【51单片机】51单片机学习笔记-课程简介
  • 权电阻网络DAC实现电压输出型数模转换Multisim电路仿真——硬件工程师笔记
  • 共射级放大电路的频率响应Multisim电路仿真——硬件工程师笔记
  • 程序计数器(PC)是什么?
  • 一个简单的分布式追踪系统
  • 【AI大模型面试八股文】大模型训练中如何应对灾难性遗忘问题?