教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 golang 切片 接口_Go指南8面对对象与接口

golang 切片 接口_Go指南8面对对象与接口

发布时间:2021-12-31   编辑:jiaochengji.com
教程集为您提供golang 切片 接口,Go指南8面对对象与接口等资源,欢迎您收藏本站,我们将为您提供最新的golang 切片 接口,Go指南8面对对象与接口资源

点击上方 “ 言淦说 ”,选择 “加为星标”

第一时间关注各种技术干货!

<h2><span style="font-weight:bold;"/>前言</h2>

在Golang中,接口(Interface)包含两层意思,一是一系列方法的集合,而是代表一种类型,比如接口类型,整数类型。

<h2><span style="font-weight:bold;"/>接口是一系列方法的集合</h2>

以我们比较熟悉的数据库为例,一个数据库一般会有打开和关闭操作,所以我们可以定义这样一个接口

<pre class="has"><code>// 数据库接口,包含 openDB 和 closeDB两个方法
type Database interface {
 openDB()
 closeDB()
}

</code></pre>

但这样定义没有用,我们还要实现这个接口,毕竟当我们存储数据的时候,需要一个明确的数据库,比如MySQL,或者MongoDB。

<pre class="has"><code>// Golang中的接口是自动实现的,当你的结构体包含接口中所有方法时,注意是所有,则Golang解释器会认为MySQL实现了 Database 这个接口
type MySQL struct {
}

func (mysql *MySQL) openDB()  {
 fmt.Println("open mysql")
}

func (mysql *MySQL) closeDB()  {
 fmt.Println("close mysql")
}

</code></pre>

当你想再扩展一个数据库时,比如MongoDB,只需实现同样的方法即可,非常方便

<pre class="has"><code>type MongoDB struct {
}

func (mongo *MongoDB) openDB()  {
 fmt.Println("open mongodb")
}

func (mongo *MongoDB) closeDB()  {
 fmt.Println("open mongodb")
}

</code></pre>

其实说了这么多,接口到底有什么用呢?它的作用就是解耦,让我们可以不用关心底层实现,还是就是方便扩展。

再举个使用的栗子,如果我们不用接口,且一开始使用的是MySQL数据库,我们的业务可能是这样子的:

<pre class="has"><code>// login.go
mysql := &MySQL{}

func login() {
 mysql.openDB()

 // 执行登录的逻辑
 mysql.checkUser()
 ...

}

func getUInfo() {
 mysql.openDB()

 // 执行逻辑
 mysql.checkUser()
 ...
}

</code></pre>

从上面的例子可以看到,如果不用接口,我们的代码会充斥着很多 <code>mysql</code>,如果有一天你需要把数据库换成 <code>MongoDB</code>,你就会发现你得把这些接口都换成MongoDB的,非常麻烦。

也许从上面的例子上看,更换数据库只是批量更换<code>mysql</code>这个字符串而已,但也许实际业务远比这个复杂得多。

而当我们使用接口后,业务代码就会变成这样子:

<pre class="has"><code>var DBT Database

func init(dbType string) {
 if dbType == "mysql" {
  DBT = &MySQL{}
 } else {
  DBT = &MongoDB{}
 }
}

func login() {
 DBT.openDB()

 // 执行登录的逻辑
 DBT.checkUser()
}

func getUInfo() {
 DBT.openDB()

 // 执行逻辑
 DBT.checkUser()
 ...
}
</code></pre>

你会发现业务代码已经没有了mysql的身影,因为对业务代码来说,它确实不需要关心我使用什么数据库,只需要关心逻辑对不对就行了。

当你想切换其他数据库时,只需要更换dbType参数,并实现Database这个接口的所有方法即可,是不是轻松了很多?

杂谈:

<ul><li>其实你也可以将接口当成一个对象,一个对象会有很多方法属性,当你实现的方法越多,你就跟这个接口(对象)越像。</li><li>接口其实代表了一种信任关系,即你相信调用它,就能给你带来想要的功能</li><li>接口之间可以互相组合,所以一个大模块就可以拆分成很多小模块,即小接口</li></ul><h2><span style="font-weight:bold;"/>接口也是一种类型</h2>

interface{} 类型是没有方法的接口。

由于这个接口没有方法,等同于其他类型(整数、字符串等)都实现了这个接口,所以我们可以看到当其作为参数时,可以传入任何类型的变量。

<pre class="has"><code>func interfaceType(data interface{}) {
 fmt.Println("data", data)
}

// 可传入字符串和整数
func interfaceTypeTest()  {
 interfaceType(111)
 interfaceType("111")
}

</code></pre>

但是,凡事都有例外,比如大多数人犯的一个错误就是定义了一个 []interface{} 参数,然后以为<code>[]int</code>和<code>[]string</code>都可以传入。

<pre class="has"><code>func sliceInterface(dataList []interface{}) {
 for _, data := range dataList {
  fmt.Println(data)
 }
}

func mapInterface(dataMap map[string]interface{}) {
 fmt.Println(dataMap)
}

// 错误的做法
func sliMapErrorInterfaceTest()  {
 nums := []int{1, 2, 3, 4}
    id2name := map[int]string{1: "aa", 2: "bb", 3: "cc"}
  
 // 报错:Cannot use 'nums'(type []int) as type []interface{}
 sliceInterface(nums)
 // 报错:Cannot use 'id2name'(type map[int]string) as type map[string]interface{}
 mapInterface(id2name)
}

</code></pre>

正确的做法是需要自己手动做一层转换,像下面的代码:

<pre class="has"><code>// 正确的做法
func sliMapCorrectInterfaceTest() {
 nums := []int{1, 2, 3, 4}
 id2name := map[int]string{1: "aa", 2: "bb", 3: "cc"}
  
  // 需手动转换
 numsI := []interface{}{}
 id2nameI := make(map[int]interface{})
 for _, num := range nums {
  numsI = append(numsI, num)
 }
 for k, v := range id2name {
  id2nameI[k] = v
 }
 
 sliceInterface(numsI)
 mapInterface(id2nameI)
}

</code></pre>

这是因为<code>[]interface{}</code> 实际是一个切片类型,只不过它的内容刚好是<code>interface{}</code>类型。这样来讲肯定还是有人不明白,所以官方文档也从内存的角度来阐述它的不同。

<code>[]interface{}</code> 中的<code>interface{}</code> 在内存中占了两个字符,第一个字符表示它包含的数据的类型,第二个字符表示所包含的数据或者指向它的指针,这也就意味着,对于

aa := []int{1, 2, 3} 可能只占 1 * 3 个字符,但是对于 aa := []interface{}{1, 2, 3} 则会占 2 * 3 = 6个字符。

所以当我们将上述的 nums 变量传递至sliceInterface时,由于类型本身就不匹配,且Go又没有对应的自动转换机制,所以就报错了。

<h3><span style="font-weight:bold;">参考资料</span></h3> [1]

官方文档: <em>https://github.com/golang/go/wiki/InterfaceSlice</em>

[2]

理解 Go interface 的 5 个关键点: <em>https://sanyuesha.com/2017/07/22/how-to-understand-go-interface/</em>

[3]

如何在 Go 中使用接口: <em>https://juejin.im/post/5a6931dc518825734501b591</em>

扫码关注我,发现更多精彩!

到此这篇关于“golang 切片 接口_Go指南8面对对象与接口”的文章就介绍到这了,更多文章或继续浏览下面的相关文章,希望大家以后多多支持JQ教程网!

您可能感兴趣的文章:
golang 切片 接口_Go指南8面对对象与接口
go 获取函数地址_Go语言基础--接口浅析
golang 切片 接口_Go 中接口值的复制
go struct 成员变量后面再加个字符串是什么意思?_Go语言的学习笔记(第十章) 接口...
Go学习笔记:接口实现与指针
第07章 Go语言接口(interface),Golang接口(interface)
GoLang学习笔记(三十四)接口及空接口
Golang interface 接口要点梳理
2020-10-18Go语言接口
【Golang】go语言面向接口

[关闭]
~ ~