说说Go中的interface
前言
面向对象语言都会有接口这个概念。go
中的接口就是interface
。它跟很多别的面向对象的接口很不一样。如java
的接口需要使用implement
来实现接口
interface定义
1 | type I interface{ |
上述代码就定义了一个go
的接口,包含了一个方法。当然也可以不包含方法。go
的interface
是一种具有一组方法的类型。如果一个类型实现了一个interface
的所有方法,我们就说该类型实现了该接口。其实go
中所有的类型都实现了empty interface
。go
不需要使用关键字来实现interface
,只需要实现interface
包含的方法即可。上述代码struct S
实现了interface I
interface多态
1 | func f(i I) { |
interface
的一个重要用途就是体现在函数的参数上。函数的参数如果是interface
,那么可以传入任意实现了该interface
的类型。这就是多态
判断interface
类型
go
可以使用value, ok := em.(T)
来检查类型
1 | if t, ok := i.(*S); ok { |
上述代码用来检查i
是否是*S
类型,如果是,那么ok
是true
。如果需要区分多种类型那么可以使用switch
1 | switch t := i.(type) { |
empty interface
如果定义一个函数,其参数是empty interface
。那么这个函数可以接受任何类型作为它的参数
1 | func doSomething(v interface{}){} |
运行时v
并不是任意类型,v
只是一个interface
。之所以可以接受任何类型是go
执行时传递到函数的任何类型都被自动转换成interface{}
。至于运行中是如何转换的,可以参考。interface{}
类型的slice
是无法接受任何类型的
1 | func printAll(vals []interface{}) { |
上述代码执行会出错,因为go
不会为interface{}
的slice做自动转换。至于为什么,可以参考
interface{}
会占用两个字长的存储空间,一个是自身的methods
数据,一个是指向其存储值的指针,也就是interface
变量存储的值,因而slice[]interface{}
其长度是固定的N2, 但是[]T的长度是Nsizeof(T),两种 slice 实际存储值的大小是有区别的。
虽然go
不能帮我们自动转换,但我们可以手动转换
1 | var dataSlice []int = foo() |
interface receiver
interface
定义时并没有严格规定实现者的方法receiver
是个value receiver
或者pointer receiver
。上述代码S
的Set Receiver
是pointer
,也就是实现I
的两个方法的receiver
一个是value
一个是pointer
。如果使用f(s)
,那么传递给f
是s
的一个拷贝。go
中的函数都是按值传递
如果是按pointer
调用,go
会自动进行转换,因为有了指针,那么总是能够得到指针指向的值,如果是值调用,那么无法得知原始值是什么
1 | type Animal interface { |
上述代码Pig
使用的是指针,如果使用值会无法运行。按值传递,函数内部对于值的任何改变都不会影响到原始值