GO语言接口使用指南

您所在的位置:网站首页 独角仙的颜色是什么 GO语言接口使用指南

GO语言接口使用指南

2023-09-09 13:50| 来源: 网络整理| 查看: 265

文章目录 接口接口的定义与使用实现的意义类型断言

接口

接口是Go语言的一种类型。简单上来讲,接口就是一系列方法的集合。通过定义接口,可以实现面向对象的多态,以及为反射提供支持。

我们可以把接口看做一个盒子,这个盒子可以装类型 与 该类型的值

接口的定义与使用

我们知道,Go语言里面声明一个接口,或者类型,有3种方式

定义型:type Doer interface { Do() }非定义型 interface { Do() }非定义型别名 type Doer = interface { Do() }

别名方式在编译器看来,和原始类型是同一类型,相互可以赋值

下面我们来看看如何实现一个接口呢

type MyInt int func (*MyInt) Do() { fmt.Println("i am do ") }

这样就实现了上面定义的那个接口了,这是一个指针接收者,可以定义一个MyInt变量去调用 Do 方法,下面列举了定义该类型和使用该类型的方法

func main() { a := new(MyInt) b := MyInt(1) var c MyInt var d MyInt = 1 var e Doer = new(MyInt) var f Doer = MyInt(1) // 编译出错,因为Do方法为指针接收,所以该接口变量无法接收一个值类型的值,只能接收指针类型的值 a.do() b.do() c.do() d.do() e.do() }

除了使用指针接收方式,还可以使用值接收方式,比如像这样

func (MyInt) Do() { fmt.Println("i am do ") }

那么这两种有何区别呢,只需要记住,「指针接收」比「值接收」更严苛,区别就在于,使用「接口变量」能包裹的值的类型是值还是指针,所以上述定义为指针接收类型的方法的时候,接口变量就不能包裹值类型,所以上述f变量那行会出现编译错误

说得通俗点,定义为值接收者 方法时,指针和值都可以调用,我们可以通过下面这两段代码测试一下

package main import ( "fmt" "reflect" ) type A struct { } func (*A) Do() { fmt.Println("call A do") } type B struct { } func (B) Do() { fmt.Println("call B do") } func main() { //A{}.Do() // 编译有错,无法使用值去调用指针接收者的方法 new(A).Do() // 正确 B{}.Do() // 值接收者时候,值和指针都能正确调用 new(B).Do() // printMethodList(A{}) // output: 无打印 printMethodList(&A{}) // output: Method Name is Do() printMethodList(B{}) // output: Method Name is Do() printMethodList(&B{}) // output: Method Name is Do() } // 打印p拥有的方法集 func printMethodList(p interface{}) { v := reflect.TypeOf(p) for i := 0; i } = 123

有一个很重要的思想方法就是,当你要赋值给一个接口变量时,你这个类型只要包含了接口所定义的方法集,那么就可以赋值给它

func Add(a, b interface{}) { if aa, ok := a.(interface{Do()}); ok { // 只要 a 这个类型有 Do 方法,则这里就可以编译通过 aa.Do() // ... } } 类型断言

其实在Go语言里面,类型的断言,就类似于Java里的类型强转差不多(使用场景),目的是,将通用型接口变量,转向更定制化的方向。下面举个例子,不过有一点必须记住,::被断言的一定是接口类型::

type Sayer interface { Say() } type People struct { Name string } func (*People) Say() { fmt.Println("我是一个人,我正在说话") } func main() { var sayer Sayer = &People{} // 因为 People 实现了 Sayer接口,所以可以赋值给该接口的变量 sayer.Say() // 调用该接口方法,此时并不管关心具体实现方式是怎样的,只要实现了就行 people := sayer.(*People) // 此时你很肯定此接口变量包裹的就是 *People 类型,所以不需要第二个参数进行判断 }

上面简单使用了一下类型断言,它的表达式有2种

v1 := i.(Type) // 确定就是这个类型的时候,无需第二个参数进行判断 v2, ok := i.(Type) // 无法确定它的类型,ok 是个 bool 值,可以通过它判断是否实现

假如使用了第一种,类型又断言错了的话,会直接产生一个 panic,用第二种则不会,可以通过 ok 值来进行判断是否实现了该接口

有时候新手肯定会纳闷,啥时候该断言呢?我的接口类型是该放外面,还是括号里面呢。

其实只要记住,断言的目的。

咱们平时说「断言」二字,其实就是就算断定的意思,你很断定这个接口的动态类型,就是某个具体的类型。

断言的时候,可以不断言出具体的类型,也就是断言的类型可以是接口 i.(另一个接口类型B) ,但是 i 和 此接口类型,一定是可以装载同一个动态类型。i 是比较通用的接口类型,而 i 中的动态类型,一定也是实现了 接口B,只不过此时我们只需要使用 接口B 的方法,所以没必要将整个动态类型断言出来(当然断言出整个动态类型也没什么错)

如果还是懵逼,可以记住下面的公式

更通用的接口.(较为定制的类型)

左边的一定是更加通用的,右边一定是更加实现的:通用 -> 实现

记住这个公式,可以避免犯错,百试不爽

[未完待续]。。。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3