go interfaces
Go语言中接口是一组方法的集合,它是Go语言的重要组成部分,面向接口编程使得代码之间的耦合度能够更低,并且更加方便测试。
# Go中的接口
Go中的接口分为两种,一种就是如上所说的方法的集合
,另一种则是类型。
go
复制代码//
// @Description: 定义接口-人
//
type person interface { // 第一种,所谓方法的集合
eat()
printName() string
}
var val interface{} // 第二种,interface{}作为一种类型
2
3
4
5
6
7
8
9
10
# 隐式实现
Go中的接口和Java中的不同,Java中需要使用关键字implement
来显式的声明一个类实现了某一个接口,而Go中则不需要。
与Java中Class
对应,Go中则使用了struct
结构体来表达类
的概念,在Go中,任意一个struct
实现了接口中的所有方法,那么则认为该struct
实现了该接口。
go
复制代码type Amy struct {
Name string
Height float64
Weight float64
Age int
}
func (receiver Amy) eat() {
// do sth
}
func (receiver Amy) printName() string {
return receiver.Name
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 接口类型
上面提到接口可以作为类型,更进一步的,接口在一定条件下可以与其他类型相互转换
。
go
复制代码type Mike struct {
Name string
}
type Jhon struct {
Name string
}
func (receiver Mike) eat() {
// do sth
}
func (receiver Mike) printName() string {
return receiver.Name
}
func (receiver Jhon) eat() {
// do sth
}
// Jhon这个struct没有实现printName这个方法
//
// @Description: 定义了一个做朋友的函数,接收的参数类型为person接口类型
// @param p1
// @param p2
//
func makeFriends(p1 person, p2 person) {
fmt.Printf("%s and %s are friends now", p1.printName(), p2.printName())
}
func main() {
makeFriends(Amy{Name: "Amy"}, Mike{Name: "Mike"}) // 正常运行打印
// Amy and Mike are friends now
makeFriends(Amy{Name: "Amy"}, Jhon{Name: "Jhon"}) // 编译出错
// cannot use Jhon literal (type Jhon) as type person in argument to makeFriends:
//Jhon does not implement person (missing printName method)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
上面的代码可以看出来,任意一个实现了person
接口的struct
都可以转换成person
类型(Amy
和Mike
实现了接口,但是Jhon
没有实现,于是报错)
而对于interface{}
类型的变量则可以与任意类型
进行转换
,这里需要注意的是,仅仅是可以转换,而并非等于任意类型
。Go中的interface{}
和C语言中的void*
有些类似,void*
可以代表任意类型
,但是interface{}
只是具备着转换成为任意类型
的能力,其本质上仍为interface{}
类型。举个例子:
go
复制代码func main() {
var w interface{} // 标记①
w = new(bytes.Buffer) // 标记②
w = new(int) // 标记③
}
2
3
4
5
6
在Goland中用debug模式来查看变量w
的类型
在标记①处,可以看到w
的类型是interface{}
,并且值为nil
在标记②处,可以看到w
的类型是{interface{}|*bytes.Buffer}
,实质上仍为一个interface{}
类型。注:关于interface{}
底层源码可以参考此篇文章 (opens new window)
在标记③处,可以看到w
的类型是{interface{}|*int}
# 类型断言
类型断言是一个使用在接口值上的操作。语法上它看起来像x.(T)被称为断言类型,这里x表示一个接口的类型和T表示一个类型。一个类型断言检查它操作对象的动态类型是否和断言的类型匹配。 引用自Go语言圣经(中文版) (opens new window)
当interface{}
类型作为函数参数时,可以传递任意类型的变量,参数在传递过程中会进行隐式的类型转换
,转换成interface{}|T
类型。由于传入参数类型的不可控性,因此常常会在函数内进行类型断言
(就是常说的类型判断)。
go
复制代码func useInterface(i interface{}) {
// 第一种方式,适合用于判断i是否为某一类型
if convert, ok := i.(float64); ok {
// do sth
}
// 第二种方式,使用switch来进行判断
switch x := i.(type) {
case float64:
// do sth
case string:
// do sth
case int32:
// do sth
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
如果直接使用x.(T)
进行断言,如果x
不是T
类型,那么则会出现panic
错误,这显然是不够优雅的,所以建议尽可能的使用convert, ok := x.(T)
或者switch + x.(type)
的方式来进行类型断言。