前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go 编程 | 连载 18 - 接口 Interface

Go 编程 | 连载 18 - 接口 Interface

作者头像
RiemannHypothesis
发布2022-09-28 16:10:07
4560
发布2022-09-28 16:10:07
举报
文章被收录于专栏:Elixir

一、Go 语言中的接口

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

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

接口的声明

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

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

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

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

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

代码语言:javascript
复制
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"
}

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

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

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

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

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

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

代码语言:javascript
复制
var fighter Fighter

一个小陷阱

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

代码语言:javascript
复制
type Course struct {
   Name string
   Price float64
}

type Outputer interface {
   Output() string
}

// 使用结构体指针作为函数接收者
func (c *Course) Output() string {
   fmt.Println(c.Name)
   return ""
}

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

image.png
image.png

二、接口是一种抽象类型

Go 中多态的实现

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

代码语言:javascript
复制
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 结构体实例

代码语言:javascript
复制
func main() {

   // 多态
   var f Fighter = Hero{"Thor, God of Thunder"}
   f.Aex()
   f.Hammer()
}

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

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

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

代码语言:javascript
复制
func main() {

   // 多态
   var f Fighter = Evil{"Thanos"}
   f.Aex()
   f.Hammer()

   fmt.Printf("%T", f)
}

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

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

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

接口作为函数参数

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

代码语言:javascript
复制
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 都实现拿起暴风战斧的功能。

代码语言:javascript
复制
func main() {

   SwingStormbreaker(f)
}

func SwingStormbreaker(f Fighter) {
   fmt.Printf("%v is swing Stormbreaker", f)
}

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

代码语言:javascript
复制
{Thanos} is swing Stormbreaker
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Go 语言中的接口
  • 二、接口是一种抽象类型
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档