为什么枚举要实现接口?
306
2022-11-02
Go struct 和 interface:结构体与接口都实现了哪些功能?
接口
• 接口定义一组方法集合
type IF interface {Method1(param_list) return_type}
• 适用场景:Kubernetes 中有大量的接口抽象和多种实现(比如定义了一堆标准接口,然后通过各个厂商来适配,每个厂商都会有自己的实现方法,框架代码的话,只需要去定义接口里面定义的这些method,至于这些method到底是谁执行就看你每个接口的实现了)
• Struct 除实现 interface 定义的接口外,还可以有额外的方法
• 一个类型可实现多个接口(Go 语言的多重继承)
• Go 语言中接口不接受属性定义
• 接口可以嵌套其他接口
定义一个统一的接口,然后用多个结构体去实现这些接口,这些结构体其实是可以加到同一个接口切片里面的,在打印的时候,也就是在调用函数的时候,go语言能够自动的判断它到底是哪一个具体的类型,然后调用其具体的实现。
注意事项
• Interface 是可能为 nil 的,所以针对 interface 的使用一定要预先判空,否则会引起程序 crash(nil panic)(interface要先赋值再去访问,那么会出现上面说的情况)
• Struct 初始化意味着空间分配,对 struct 的引用不会出现空指针
接口
接口的定义
接口是和调用方的一种约定,它是一个高度抽象的类型,不用和具体的实现细节绑定在一起。接口要做的是定义好约定,告诉调用方自己可以做什么,但不用知道它的内部实现,这和我们见到的具体的类型如 int、map、slice 等不一样。
接口的定义和结构体稍微有些差别,虽然都以 type 关键字开始,但接口的关键字是 interface,表示自定义的类型是一个接口。也就是说 Stringer 是一个接口,它有一个方法 String() string,整体如下面的代码所示:
type Stringer interface { String() string}
提示:Stringer 是 Go SDK 的一个接口,属于 fmt 包。
针对 Stringer 接口来说,它会告诉调用者可以通过它的 String() 方法获取一个字符串,这就是接口的约定。至于这个字符串怎么获得的,长什么样,接口不关心,调用者也不用关心,因为这些是由接口实现者来做的。
接口的实现 接口的实现者必须是一个具体的类型,继续以 person 结构体为例,让它来实现 Stringer 接口,如下代码所示:
func (p person) String() string{ return fmt.Sprintf("the name is %s,age is %d",p.name,p.age)}
给结构体类型 person 定义一个方法,这个方法和接口里方法的签名(名称、参数和返回值)一样,这样结构体 person 就实现了 Stringer 接口。
注意:如果一个接口有多个方法,那么需要实现接口的每个方法才算是实现了这个接口。
实现了 Stringer 接口后就可以使用了。首先我先来定义一个可以打印 Stringer 接口的函数,如下所示:
func printString(s fmt.Stringer){ fmt.Println(s.String())}
这个被定义的函数 printString,它接收一个 Stringer 接口类型的参数,然后打印出 Stringer 接口的 String 方法返回的字符串。
printString 这个函数的优势就在于它是面向接口编程的,只要一个类型实现了 Stringer 接口,都可以打印出对应的字符串,而不用管具体的类型实现。
因为 person 实现了 Stringer 接口,所以变量 p 可以作为函数 printString 的参数,可以用如下方式打印:
printString(p)
结果为:
the name is 飞雪无情,age is 30
现在让结构体 address 也实现 Stringer 接口,如下面的代码所示:
func (addr address) String() string{ return fmt.Sprintf("the addr is %s%s",addr.province,addr.city)}
因为结构体 address 也实现了 Stringer 接口,所以 printString 函数不用做任何改变,可以直接被使用,打印出地址,如下所示:
printString(addr)//输出:the addr is 北京北京
这就是面向接口的好处,只要定义和调用双方满足约定,就可以使用,而不用管具体实现。接口的实现者也可以更好的升级重构,而不会有任何影响,因为接口约定没有变。
值接收者和指针接收者 我们已经知道,如果要实现一个接口,必须实现这个接口提供的所有方法,我们也知道定义一个方法,有值类型接收者和指针类型接收者两种。二者都可以调用方法,因为 Go 语言编译器自动做了转换,所以值类型接收者和指针类型接收者是等价的。
但是在接口的实现中,值类型接收者和指针类型接收者不一样,下面我会详细分析二者的区别。
在上一小节中,已经验证了结构体类型实现了 Stringer 接口,那么结构体对应的指针是否也实现了该接口呢?我通过下面这个代码进行测试:
printString(&p)
测试后会发现,把变量 p 的指针作为实参传给 printString 函数也是可以的,编译运行都正常。这就证明了以值类型接收者实现接口的时候,不管是类型本身,还是该类型的指针类型,都实现了该接口。
示例中值接收者(p person)实现了 Stringer 接口,那么类型 person 和它的指针类型*person就都实现了 Stringer 接口。
现在,我把接收者改成指针类型,如下代码所示:
func (p *person) String() string{ return fmt.Sprintf("the name is %s,age is %d",p.name,p.age)}
修改成指针类型接收者后会发现,示例中这行 printString(p) 代码编译不通过,提示如下错误:
./main.go:17:13: cannot use p (type person) as type fmt.Stringer in argument to printString: person does not implement fmt.Stringer (String method has pointer receiver)
意思就是类型 person 没有实现 Stringer 接口。这就证明了以指针类型接收者实现接口的时候,只有对应的指针类型才被认为实现了该接口。
我用如下表格为你总结这两种接收者类型的接口实现规则:
可以这样解读:
当值类型作为接收者时,person 类型和*person类型都实现了该接口。当指针类型作为接收者时,只有*person类型实现了该接口。
可以发现,实现接口的类型都有*person,这也表明指针类型比较万能,不管哪一种接收者,它都能实现该接口。
---------------------------------------------------------------------
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~