说说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使用的是指针,如果使用值会无法运行。按值传递,函数内部对于值的任何改变都不会影响到原始值