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


相关文章
|
11月前
|
存储 Go
Go语言之接口与多态 -《Go语言实战指南》
Go 语言中的接口是实现多态的核心机制,通过一组方法签名定义行为。任何类型只要实现接口的所有方法即视为实现该接口,无需显式声明。本文从接口定义、使用、底层机制、组合、动态行为到工厂模式全面解析其特性与应用,帮助理解 Go 的面向接口编程思想及注意事项(如 `nil` 陷阱)。
298 22
|
11月前
|
人工智能 数据可视化 编译器
Go interface实现分析
本文深入探讨了Go语言中接口的定义、实现及性能影响。接口作为一种“约定”,包含方法签名集合,无需依赖具体类型即可调用方法,隐藏了内部实现细节。文章分析了接口的两种实现方式(iface和eface)、按值与按指针实现的区别,以及nil接口与普通nil的区别。同时,通过反汇编代码对比了接口动态调用与类型直接调用的性能差异,指出接口调用存在内存逃逸和无法内联的问题。最后总结了接口的优势与局限性,强调在实际开发中需根据场景合理选择是否使用接口。
283 13
|
6月前
|
Java 编译器 Go
【Golang】(5)Go基础的进阶知识!带你认识迭代器与类型以及声明并使用接口与泛型!
好烦好烦好烦!你是否还在为弄不懂Go中的泛型和接口而烦恼?是否还在苦恼思考迭代器的运行方式和意义?本篇文章将带你了解Go的接口与泛型,还有迭代器的使用,附送类型断言的解释
305 4
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
11月前
|
存储 JSON Go
Go语言之空接口与类型断言
本文介绍了 Go 语言中空接口(`interface{}`)和类型断言的核心概念及其应用。空接口可存储任意类型数据,适用于通用函数、动态数据结构与 JSON 解析等场景;类型断言用于将接口变量还原为具体类型,推荐使用带 `ok` 的写法以避免程序崩溃。此外,文章通过示例讲解了 `type switch` 类型判断与 JSON 处理技巧,并总结了空接口的注意事项,强调滥用可能导致类型安全性降低。内容深入浅出,帮助开发者灵活运用这些特性。
314 15
|
11月前
|
Go
Go语言接口的定义与实现
Go 语言的接口提供了一种灵活的多态机制,支持隐式实现和抽象编程。本文介绍了接口的基本定义、实现方式、空接口的使用、类型断言以及接口组合等核心概念,并探讨了接口与 nil 的关系及应用场景。通过示例代码详细说明了如何利用接口提升代码的可扩展性和可测试性,总结了接口的关键特性及其在依赖注入、规范定义和多态调用中的重要作用。
408 14
|
11月前
|
设计模式 缓存 算法
Go如何进行高质量编程与性能调优实践
本文介绍了Go语言高质量编程与性能调优的实践方法。高质量编程包括良好的编码习惯(如清晰注释、命名规范)、代码风格与设计(如MVC模式)、简洁明了的代码原则,以及单元测试与代码重构的重要性。性能调优方面,涵盖算法优化、数据结构选择、I/O优化、内存管理、并行与并发处理优化及代码层面的改进。通过这些方法,可有效提升代码质量和系统性能。
217 13
|
11月前
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
257 10
|
数据采集 监控 Java
go语言编程学习
【11月更文挑战第3天】
301 7
|
数据库连接 Go 数据库
Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性
本文探讨了Go语言中的错误注入与防御编程。错误注入通过模拟网络故障、数据库错误等,测试系统稳定性;防御编程则强调在编码时考虑各种错误情况,确保程序健壮性。文章详细介绍了这两种技术在Go语言中的实现方法及其重要性,旨在提升软件质量和可靠性。
269 1