首页 > 其他分享 >「Go」 通过一个案例解释清楚 interface 的用法和注意事项

「Go」 通过一个案例解释清楚 interface 的用法和注意事项

时间:2023-03-14 11:35:03浏览次数:66  
标签:接口 interface 鸭子 func 注意事项 Go type Eat

Golang 接口(interface) 简介和入门使用

如果在 公众号 文章发现状态为 已更新, 建议点击 查看原文 查看最新内容。

状态: 未更新

原文链接: https://typonotes.com/posts/2023/02/28/devopscamp-interface-sample/

上下文 interface 应该是 Go语言 中一个极其重要的 基石 概念了。

这里有一篇 Go 语言设计与实现 - 接口 interface , 是目前我学习的资料中 完成度友善度 都很高的一篇文章。

go v1.18 中, interface 有了一个别名 any。 所以在说 interface 和 any 的时候, 其实说的是相同的东西。

interface 的使用场景到处都是。 还记得我们之前说的 JSONYAML 的配置文件处理吗?

// json.Marshal
func Marshal(v any) ([]byte, error) {
// ....
}

看完文章, 你至少应该知道

  1. 什么是 鸭式对象
  2. 怎么使用 接口断言

鸭式对象

先来说说 鸭式对象: 叫的像鸭子, 走路像鸭子, 那它就是鸭子。

  1. 我们对鸭子下一个定义, 1. 发出嘎嘎的叫声, 2. 走路一摇一摆, 然后规定 任何满足这两个条件的 都是鸭子
  2. 我们拿着定义去对比。
    1. 有一个 是演员, 他能模仿鸭子发出嘎嘎声, 也能模仿鸭子一摇一摆的走路。 那么, 这个人能被认为是鸭子。
    2. 有一个 玩具 , 能发出嘎嘎声, 也能一摇一摆的就走。 那么, 这个玩具也能被认为是鸭子。

简单的说, 接口就是 白名单定义, 满足白名单要求就行。

在上面描述中, 已经提到了接口的两个重要概念: 定义 与 实现。

(1) 叫做 接口定义

type Duck interface{
    Quack()
    Walk()
}

(2) 叫做 接口实现

// 人
type Person struct{}
func (p *Person) Quack(){}
func (p *Person) Walk(){}

// 玩具
type Toy struct{}
func (t *Toy) Quack(){}
func (t *Toy) Walk(){}

通过案例讲解

我们知道, 人和大熊猫都是哺乳动物

  1. 他们都会 吃 (Eat) 吃东西, 区别是 人吃饭, 熊猫吃竹子
  2. 更大的不同是, 人会 读书 (Read), 熊猫不会。
func Eating(v any){
    v.Eat() // output: ???
}

func WhoAreYou(v any){
    // ???? 
}

接口定义

我们回到作业要求, 要求实现 动物 两种接口。 要求

  1. 动物接口需要实现 这个动作。
type Animal interface{
    Eat()
}
  1. 人接口 除了需要吃之外 , 还需要 读书 这个动作。
type Human interface{
    Eat()
    Read()
}

这种 直接为人定义两种方法 的方式是可以的, 但是当以后我们要扩展动物接口, 添加 Walk 的时候, 也必须要为人添加 Walk 才行。 久而久之, 不仅难以管理, 还无法从字面值上看到人和动物的关系。

那有没有更简单的方式呢? 有! 接口嵌套, Go 语言中没有继承概念。

type Human interface{
    Read()
    Animal
}

接口实现

我们定两个 struct, 分别是 Panda 和 Child

type Panda struct{}

func (p *Panda) Eat(){
    fmt.Println("熊猫吃竹子")
}

// 

type Child struct{}

func (c *Child) Eat(){
    fmt.Println("人吃饭")
}

func (c *Child) Read(){
    fmt.Println("人还会读书")
}

这样, 小孩和熊猫的结构对象就定义好了, 他们实现了各自的方法。 并且满足之前人和动物的接口。

接口检查

在书写代码的过程中, 要检查一个结构对象是否完全 实现了接口对应的所有方法, 避免在运行调用的时候才发现。

可以使用以下代码

var _ Person = &Child{}

这是一个 变量定义并赋值 的语句。 特殊的地方在于, 变量名使用了 **下划线_**。

var peppa Person = &Child{} // 把 _ 换成了 peppa 

如此操作

  1. 编译器在书写的时候就会进行语法检查。
  2. 创建的 不存在变量 最终会被丢弃。

接口断言

在日常使用中, 我们可以通过 断言接口A 转换成 接口B

类似的, 就像问一头 熊猫 是不是

func WhoAreYou(v any){
    animal, ok := v.(Animal)  // 断言, 判断 any 是否为能转换为 Animal
    if ok {
        animal.Eat()
    }

    child, ok := animal.(Human) // 断言
    if ok {
        child.Read()
    }
}

断言 返回的 第二个结果(ok) 是可以省略的。 不过这种用法需要用在我们能 保证 转换一定成功的情况下。

animal := child.(Animal)

在这里, Human 嵌套了 Animal 方法, 所以转换一定成功。

互相吹捧, 共同进步

欢迎和我一起学习进步, 如果有什么问题, 可以给我私信留言。 或者

公众号

标签:接口,interface,鸭子,func,注意事项,Go,type,Eat
From: https://blog.51cto.com/bashrc/6120133

相关文章

  • GO语言学习笔记-测试篇 Study for Go ! Chapter ten- Test
    持续更新Go语言学习进度中......GO语言学习笔记-类型篇StudyforGo!Chapterone-Type-slowlydance2me-博客园(cnblogs.com)GO语言学习笔记-表达式篇Study......
  • Django基于一对多的正向查询和反向查询
    1.正向查询obj=models.User.objects.get(name='longge')name=obj.group.nameprint(name)#肖邦组2.反向查询"""反向查询"""obj=models.Group.objects......
  • 99Go语言基础
    输入输出packagemainimport"fmt"funcmain(){ /** 输入和输出:fmt包 输出: fmt.Println()//打印换行 fmt.Print()//打印 fmt.Printf()//格式化打......
  • go timer踩坑记录,为什么,如何正确使用
    业务需求:开一个循环处理外部发来的任务,如果一定时间内没有任务发过来就退出,处理完一个任务后刷新超时时间然后我就写了这个funchandle(reqCh<-chanint){ a:=time.......
  • Google Guava Cache
    JAVA8guava31.1-jre--- 序章Guavaisasuiteofcoreandexpandedlibrariesthatincludeutilityclasses,Google'scollections,I/Oclasses,andmuchmor......
  • go的泛型链表
    packagemainimport"fmt"funcMapKeys[Kcomparable,Vany](mmap[K]V)[]K{ r:=make([]K,0,len(m)) fmt.Printf("001%v,%T\n",r,r) fork:=rangem{ ......
  • django离线调用
    importdjangoimportsysimportos#获取当前项目的根路径base_path=os.path.basename(os.path.basename(os.path.abspath(__file__)))print(base_path)#将当......
  • 【django-vue】七牛云上传视频 搜索接口 支付宝sdk二次封装 下单接口 前端支付页
    目录上节回顾课程详情接口choice字段今日内容1文件存储1.1七牛云上传文件2搜索导航栏2.1Header.vue3搜索接口4搜索页面5支付宝支付介绍5.1支付测试,生成支付链接6......
  • 【建造者设计模式详解】Java/JS/Go/Python/TS不同语言实现
    简介建造者模式(BuilderPattern),也叫生成器模式,属于创建型模式。它使用多个简单的对象一步一步构建成一个复杂的对象。它允许你使用相同的创建代码生成不同类型和形式的对......
  • go mmap
    packagemainimport("fmt""os""syscall")funcF(a,buint64)uint64{r:=make([]uint64,1)cadd(r,9,8)returna+b+r[0]}funccalls(res[]ui......