Golang
Golang接口
11-25 09:01一、前言
熟读并理解此篇文章,基本可以在语法层面全面熟悉Golang的接口用法
二、什么时Golang接口
在Go语言中,接口(Interface)是一种类型,它规定了对象的行为。Golang接口是一种特殊的类型,它定义了一个对象应该拥有哪些方法。如果一个对象拥有接口要求的所有方法,那么这个对象就实现了该接口。
interface类型可以定义一组方法,但是这些方法不需要实现。并且interface不能包含任何变量,到某个自定义类型要使用的时候,再根据具体情况把这些方法写出来(实现方法)
Golang的接口非常灵活,耦合性很小,对Golang来说,它不关心要实现哪个接口,而是关心实现了哪个方法
三、接口基本语法
1. 声明接口:
type 接口名 interface {
method1(参数列表) 返回值列表
method2(参数列表) 返回值列表
}
2. 实现接口:
type 自定义类型 struct{}
func (n 自定义类型) method1(参数列表) 返回值列表 {
//方法实现
}
func (n 自定义类型) method2(参数列表) 返回值列表 {
//方法实现
}
3、使用接口:
func main() {
var m 自定义类型
var i 接口名 = m
m.method1(参数列表)
m.method1(参数列表)
}
四、接口快速入门 - 体验案例
package main
import "fmt"
//定义(声明)一个 Usb 接口,抽象出通过Usb接口可以做的事情
type Usb interface {
//声明两个没有实现的方法(接口中不能有方法体)
Start()
Stop()
}
//定义 Phone 类型,让 Phone 实现 Usb 接口的方法
type Phone struct { }
func (phone Phone) Start() { //让 Phone 实现 Usb 接口的 Start() 方法
fmt.Println("手机开始工作...")
}
func (phone Phone) Stop() { //让 Phone 实现 Usb 接口的 Stop() 方法
fmt.Println("手机停止工作...")
}
//定义 Camera 类型,让 Camera 实现 Usb 接口的方法
type Camera struct {}
func (camera Camera) Start() {
fmt.Println("相机开始工作...")
}
func (camera Camera) Stop() {
fmt.Println("相机停止工作...")
}
//方法可以比接口中定义的多,但是不能少(接口中定义的方法,struct中必须全部实现,否则报错)
func (camera Camera) Print() {
fmt.Println("打印相片")
}
//编写一个 Working 方法,接收一个 Usb 接口类型的变量
type Computer struct { }
//实现 Usb 接口就是指实现了 Usb 接口声明的所有方法
func (computer Computer) Working(usb Usb) {
//通过 usb (接口类型)变量调用 start() 和 stop() 方法
usb.Start()
usb.Stop()
}
func main() {
//创建结构体变量
var phone Phone{}
camera := Camera
computer := new(Computer)
//Working方法中的usb变量,会根据传入的实参,调用对应类型中的方法
computer.Working(phone)
computer.Working(camera)
}
通过上面的快速入门案例,总结两点
1、Golang接口中所有的方法都没有方法体,即:接口的方法都是没有实现的方法。接口体现了程序设计时多态和高内聚低耦合的思想
2、Golang中的接口,不需要显式的实现,只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。因此,Golang中没有 extends 或 implement 等关键字
五、接口使用细节和注意事项
1、接口变量,可以指向一个实现了该接口的自定义类型的变量(实例)

2、接口本身不能创建实例

3、Golang接口中不能有任何变量

4、接口中的所有方法都没有方法体,即都是没有实现的方法(加入方法体会报错)



5、接口不能调用结构体中的字段,但可以通过声明的方法,操作字段

通过自定义方法操作字段

6、接口 interface 类型默认是一个指针(引用类型),它可看做自定义类型,如果没有对 interface 初始化就使用,那么使用此类型的变量默认值是nil

7、一个自定义类型需要将某个接口的所有方法都实现,这时可以说这个自定义类型实现了该接口
假设一个接口有三个方法,一个自定义类型只有这个接口的两个方法,那也不能说实现了这个接口(必须每个都实现,缺一不可)

8、自定义类型实现某个接口时,除实现接口声明的方法外,自定义类型内部还可以有其它方法和字段,可以正常操作这些额外的字段或方法,接口变量也能正常指向这个自定义类型变量

9、一个自定义类型的只有实现了某个接口,才能将该自定义类型的变量赋值给该接口类型,自定义类型若没有实现接口就赋值(缺少接口中声明的某些方法),会报错

10、只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型

对上面代码的说明:integer 对应的类型是 Integer,Integer 又实现了 Say() 和 Print() 方法,等于 Integer 间接实现了 Interface 接口
11、一个自定义类型可以实现多个接口(有接口A声明的所有方法,也有接口B声明的所有方法,那就相当于把接口A和B都实现了)

注意:同时实现多个接口时,只能调用本接口声明的方法,不能随意穿插调用
12、接口之间也可以有继承关系,官方描述中没有“接口继承”的概念,但可以通俗理解为继承,实际操作就是 “接口嵌入”(将一个接口作为另一个接口的字段,实现接口能力的组合)
一个接口可以继承(嵌入)多个其它接口,除继承外还可以有自己的方法,这时如果要实现这个接口,必须将这个接口以及所继承接口中的方法全部都实现,若缺少未实现的方法,运行时会报错

将继承接口中的方法都实现,才算完全实现了接口

通过以上可以发现,自定义类型实现了有继承的接口,等于同时把被继承的接口也实现了,
比如上面的代码,OInterface 接口继承了 MInterface 和 NInterface 两个接口,自定义类型 O 实现了 OInterface 接口,所以 O 也同时实现了 MInterface 和 NInterface 两个接口
13、当嵌入的多个接口存在方法名相同但签名不同的情况时,会触发编译错误;若方法名和签名完全一致,则属于 “方法合并”,不会冲突

可以同时没有参数,也不会冲突

嵌入的多个接口存在方法名相同但签名不同的情况时,会触发编译错误

14、当接口定义的被自定义类型实现,且自定义类型方法的receiver为指针时,注意以下
1、使用时需对自定义类型变量携带取地址符
2、虽然是接口类型调用的方法,但依然可以修改自定义类型变量初始化时的字段的值

15、空接口 interface{} 没有任何方法,所以所有类型都实现了空接口,即:可以把任何一个变量赋给空接口
案例一:

案例二:

注意:空接口变量被赋值自定义类型后,不能调用自定义类型的字段和方法,否则报错


16、数组接口的使用方法(结合上面快速入门案例的 Usb 接口,以及实现 Usb 接口的 Phone 和 Camera 结构体)

六、接口 vs 继承(结构体组合)
实际开发中,接口和继承经常结合使用 —— 用继承实现复用代码,用接口实现多态
继承的价值主要在于:解决代码的复用性和可维护性
接口的价值主要在于:设计好各种规范(方法),让其它自定义类型去实现这些方法
接口比继承更加灵活,继承是满足“is-a”(狗是动物)的关系,接口需满足“like-a”的关系

七、接口经典案例
未完..