Go 编程 | 连载 18 - 接口 Interface

简介: Go 编程 | 连载 18 - 接口 Interface

一、Go 语言中的接口

很多编程语言中都有接口的概念,静态语言 Java 中的接口和 Go 中的接口地位或者概念是不一样的,Go 语言中的接口与 Python 中的接口比较像。

Go 中的接口是一种协议,既调用方和实现方均需要遵守的一种协议,按照统一的方法命名参数类型和数量来协调逻辑处理的过程。

接口的声明

接口是一种协议,一种规范;定义接口时只需定义规范无须关心实现的细节。

type 接口名 interface {
    方法1 (参数列表) 返回值列表
    方法2 (参数列表) 返回值列表
    ...
}
复制代码

在 Go 语言中 interface 名字仍然以单个词为优先。命名基本采用驼峰命名法,首字母根据访问控制大写或者小写。对于拥有唯一方法或通过多个拥有唯一方法的接口组合而成的接口,Go 语言的惯例是一般用"方法名+er"的方式为 interface 命名,例如 Reader、Writer 等。

Go 是区分大小写的,当方法名首字母和接口名首字母都是大些时,这个方法可以被接口所在的包之外的代码访问。

接口方法中参数列表和返回值列表中的变量名可以忽略。

func main() {
   thor := Hero{"Thor, God of Thunder"}
   thor.Hammer()
   thor.Aex()
}
type Fighter interface {
   Hammer() string
   Aex() string
}
type Hero struct {
   Name string
}
func (hero Hero) Hammer() string {
   fmt.Printf("%v 正在使用喵喵锤\n", hero.Name)
   return "Hammer"
}
func (hero Hero) Aex() string {
   fmt.Printf("%v 正在使用暴风战斧\n", hero.Name)
   return "Aex"
}
复制代码

执行上述代码,输出结果如下:

Thor, God of Thunder 正在使用喵喵锤
Thor, God of Thunder 正在使用暴风战斧
复制代码

结构体实现一个接口就需要实现接口中的所有方法,实现方法需要保持方法签名一致,包括方法名称、参数列表、返回值列表,只要有一个不一致都不能算是实现这个接口,并且在调用时会导致报错。

比如方法名不一致会报错:

# command-line-arguments
./ex9.go:9:7: thor.Aex undefined (type Hero has no field or method Aex)
复制代码

Go 语言中的接口也是一种类型,可以直接声明一个接口类型的变量

var fighter Fighter
复制代码

一个小陷阱

在实现接口方法时,方法的接收者如果是结构体指针,那么在给接口变量赋值时就只能赋值结构体指针类型,如果还是赋值结构体实例化对象怎会报错。

type Course struct {
   Name string
   Price float64
}
type Outputer interface {
   Output() string
}
// 使用结构体指针作为函数接收者
func (c *Course) Output() string {
   fmt.Println(c.Name)
   return ""
}
复制代码

结构体实现接口方法时函数接收者使用指针形式,在将实例化结构体赋值给接口变量时会报错,如下图所示:

image.png

二、接口是一种抽象类型

Go 中多态的实现

保持 Hero 结构体不变,在增加一个 Evil 结构体也实现 Fighter 接口的两个方法

type Evil struct {
   Name string
}
func (evil Evil) Hammer() string {
   fmt.Printf("%v 正在使用喵喵锤\n", evil.Name)
   return "Evil - Hammer"
}
func (evil Evil) Aex() string {
   fmt.Printf("%v 正在使用暴风战斧\n", evil.Name)
   return "Evil - Aex"
}
复制代码

在 main 方法中声明一个 Fighter 接口变量,并赋值一个 Hero 结构体实例

func main() {
   // 多态
   var f Fighter = Hero{"Thor, God of Thunder"}
   f.Aex()
   f.Hammer()
}
复制代码

执行上述代码,输出结果如下:

Thor, God of Thunder 正在使用暴风战斧
Thor, God of Thunder 正在使用喵喵锤
复制代码

上述代码中将结构体实例赋值给一个接口类型变量,实现基于接口的调用,而不是实例化对象本身的调用,如果接口类型变量赋的值不是 Hero 结构体的实例化对象,而是 Evil 结构体的实例化对象,只需更改赋值即可实现 Evil 结构体对 Fighter 接口方法的调用。

func main() {
   // 多态
   var f Fighter = Evil{"Thanos"}
   f.Aex()
   f.Hammer()
   fmt.Printf("%T", f)
}
复制代码

执行上述代码,输出结果如下:

Thanos 正在使用暴风战斧
Thanos 正在使用喵喵锤
main.Evil
复制代码

接口实际的类型就是赋值的结构体类型。

接口作为函数参数

接口也可以作为函数的参数,比如我们定义两个方法 HeroSwingStormbreaker 和 EvilSwingStormbreaker,不管是 Hero 还 Evil 都可以拿起暴风战斧,两个函数实现的功能完全是一致的,这种方式就会导致代码的冗余。

func HeroSwingStormbreaker(hero Hero) {
   fmt.Printf("%v is swing Stormbreaker", hero)
}
func EvilSwingStormbreaker(evil Evil) {
   fmt.Printf("%v is swing Stormbreaker", evil)
}
复制代码

如果只定义一个方法,并且将 Fighter 接口作为参数,在 Hero 和 Evil 都实现 Fighter 接口的前提下,Hero 和 Evil 都实现拿起暴风战斧的功能。

func main() {
   SwingStormbreaker(f)
}
func SwingStormbreaker(f Fighter) {
   fmt.Printf("%v is swing Stormbreaker", f)
}
复制代码

执行上述代码,输出结果如下:

{Thanos} is swing Stormbreaker


相关文章
|
17天前
|
存储 Rust Go
Go nil 空结构体 空接口有什么区别?
本文介绍了Go语言中的`nil`、空结构体和空接口的区别。`nil`是预定义的零值变量,适用于指针、管道等类型;空结构体大小为0,多个空结构体实例指向同一地址;空接口由`_type`和`data`字段组成,仅当两者均为`nil`时,空接口才为`nil`。
Go nil 空结构体 空接口有什么区别?
|
12天前
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
30 7
|
17天前
|
Unix Linux Go
go进阶编程:Golang中的文件与文件夹操作指南
本文详细介绍了Golang中文件与文件夹的基本操作,包括读取、写入、创建、删除和遍历等。通过示例代码展示了如何使用`os`和`io/ioutil`包进行文件操作,并强调了错误处理、权限控制和路径问题的重要性。适合初学者和有经验的开发者参考。
|
25天前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
47 7
|
1月前
|
Go 数据处理 调度
Go语言中的并发模型:解锁高效并行编程的秘诀
本文将探讨Go语言中独特的并发模型及其在现代软件开发中的应用。通过深入分析 Goroutines 和 Channels,我们将揭示这一模型如何简化并行编程,提升应用性能,并改变开发者处理并发任务的方式。不同于传统多线程编程,Go的并发方法以其简洁性和高效性脱颖而出,为开发者提供了一种全新的编程范式。
|
2月前
|
存储 Go
Go to Learn Go之接口
Go to Learn Go之接口
31 7
|
1月前
|
并行计算 算法 搜索推荐
探索Go语言的高并发编程与性能优化
【10月更文挑战第10天】探索Go语言的高并发编程与性能优化
|
2月前
|
存储 缓存 Go
go语言编程系列(五)
go语言编程系列(五)
|
2月前
|
搜索推荐 Java 编译器
go语言编程系列(四)
go语言编程系列(四)
|
2月前
|
存储 JSON 安全
go语言编程系列(七)
go语言编程系列(七)