说说Go中的interface

前言

面向对象语言都会有接口这个概念。go中的接口就是interface。它跟很多别的面向对象的接口很不一样。如java的接口需要使用implement来实现接口

interface定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type I interface{
Get() string
Set(string)
}

type S struct{
val string
}

func (s S) Get() string{
return s.val
}

func (s *S) Set(val string) {
s.val = val
}

上述代码就定义了一个go的接口,包含了一个方法。当然也可以不包含方法。gointerface是一种具有一组方法的类型。如果一个类型实现了一个interface的所有方法,我们就说该类型实现了该接口。其实go中所有的类型都实现了empty interfacego不需要使用关键字来实现interface,只需要实现interface包含的方法即可。上述代码struct S实现了interface I

interface多态

1
2
3
4
5
6
7
8
9
func f(i I) {
i.Set(10)
fmt.Println(i.Get())
}

func main() {
s := S{}
f(&s)
}

interface的一个重要用途就是体现在函数的参数上。函数的参数如果是interface,那么可以传入任意实现了该interface的类型。这就是多态

判断interface类型

go可以使用value, ok := em.(T)来检查类型

1
2
3
if t, ok := i.(*S); ok {
fmt.Println("s implements I", t)
}

上述代码用来检查i是否是*S类型,如果是,那么oktrue。如果需要区分多种类型那么可以使用switch

1
2
3
4
5
6
switch t := i.(type) {
case *T:
fmt.Println("i store *T", t)
case *S:
fmt.Println("i store *S", t)
}

empty interface

如果定义一个函数,其参数是empty interface。那么这个函数可以接受任何类型作为它的参数

1
func doSomething(v interface{}){}

运行时v并不是任意类型,v只是一个interface。之所以可以接受任何类型是go执行时传递到函数的任何类型都被自动转换成interface{}。至于运行中是如何转换的,可以参考interface{}类型的slice是无法接受任何类型的

1
2
3
4
5
6
7
8
9
10
func printAll(vals []interface{}) {
for _, val := range vals {
fmt.Println(val)
}
}

func testInterfaceSlice() {
vals := []string{"stanley", "david", "oscar"}
printAll(vals)
}

上述代码执行会出错,因为go不会为interface{}的slice做自动转换。至于为什么,可以参考

interface{} 会占用两个字长的存储空间,一个是自身的methods数据,一个是指向其存储值的指针,也就是interface变量存储的值,因而slice[]interface{}其长度是固定的N2, 但是[]T的长度是Nsizeof(T),两种 slice 实际存储值的大小是有区别的。

虽然go不能帮我们自动转换,但我们可以手动转换

1
2
3
4
5
var dataSlice []int = foo()
var interfaceSlice []interface{} = make([]interface{}, len(dataSlice))
for i, d := range dataSlice {
interfaceSlice[i] = d
}

interface receiver

interface定义时并没有严格规定实现者的方法receiver是个value receiver或者pointer receiver。上述代码SSet Receiverpointer,也就是实现I的两个方法的receiver一个是value一个是pointer。如果使用f(s),那么传递给fs的一个拷贝。go中的函数都是按值传递

如果是按pointer调用,go会自动进行转换,因为有了指针,那么总是能够得到指针指向的值,如果是值调用,那么无法得知原始值是什么

1
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
type Animal interface {
Speak() string
}

type Dog struct{}

func (d Dog) Speak() string {
return "wangwang!"
}

type Cat struct{}

func (c Cat) Speak() string {
return "miao"
}

type Pig struct{}

func (p *Pig) Speak() string {
return "keng"
}

type Programmer struct{}

func (p Programmer) Speak() string {
return "Hello, world"
}

func testAnimal() {
animals := []Animal{Dog{}, Cat{}, &Pig{}, Programmer{}}
for _, animal := range animals {
fmt.Println(animal.Speak())
}
}

上述代码Pig使用的是指针,如果使用值会无法运行。按值传递,函数内部对于值的任何改变都不会影响到原始值