空接口是指没有定义任何接口方法的接口。没有定义任何接口方法,意味着Go中的任意对象均可以实现空接口(由于没方法须要实现),任意对象均可以保存到空接口实例变量中。数据结构
空接口的定义方式:app
type empty_int interface { }
一般会简写为type empty_int interface{}
。函数
更常见的,会直接使用interface{}
做为一种类型,表示空接口。例如:布局
// 声明一个空接口实例 var i interface{}
再好比函数使用空接口类型参数:指针
func myfunc(i interface{})
在Go中不少地方都使用空接口类型的参数,用的最多的fmt
中的Print类方法:code
$ go doc fmt Println func Println(a ...interface{}) (n int, err error)
能够定义一个空接口类型的array、slice、map、struct等,这样它们就能够用来存听任意类型的对象,由于任意类型都实现了空接口。对象
例如,建立一个空接口的slice:接口
package main import "fmt" func main() { any := make([]interface{}, 5) any[0] = 11 any[1] = "hello world" any[2] = []int{11, 22, 33, 44} for _, value := range any { fmt.Println(value) } }
输出结果:内存
11 hello world [11 22 33 44] <nil> <nil>
显然,经过空接口类型,Go也能像其它动态语言同样,在数据结构中存储任意类型的数据。编译
再好比,某个struct中,若是有一个字段想存储任意类型的数据,就能够将这个字段的类型设置为空接口:
type my_struct struct { anything interface{} anythings []interface{} }
前面解释了任意类型的对象都能赋值给空接口实例。
var any interface{} any = "hello world" any = 11
空接口是一种接口,它是一种指针类型的数据类型,虽然不严谨,但它确实保存了两个指针,一个是对象的类型(或iTable),一个是对象的值。因此上面的赋值过程是让空接口any保存各个数据对象的类型和对象的值。
换一种角度考虑,空接口有本身的内存布局方式:两个指针,占用两个机器字长。
Golang给的一个经典的示例:将某个slice中的数据拷贝到空接口slice中将报错。
package main import "fmt" func main() { testSlice := []int{11,22,33,44} // 成功拷贝 var newSlice []int newSlice = testSlice fmt.Println(newSlice) // 拷贝失败 var any []interface{} any = testSlice fmt.Println(any) }
这是由于每一个空接口的内存布局都占用两个机器字长的内容。对于长度为N的空接口slice来讲,它的每一个元素都是以2机器字长为单元的连续空间,共占用N*2
个机器字长的空间。
而普通的slice,例如上面的testSlice,它的每一个元素是int类型的,int类型的内存布局和空接口不同。
这些对象的内存布局在编译期间就已经肯定好了,因此无法直接将不一样内存布局的数据结构进行拷贝。
要想完成期待的拷贝,可使用for-range的方式,将testSlice中的每一个元素赋值给空接口slice的空接口元素:也就是一个个的空接口实例。
var any []interface{} for _,value := range testSlice{ any = append(any,value) }
这样,空接口Slice中的每一个空接口实例都指向更底层的各个数据对象。而不是像前面错误的拷贝方式:每一个空接口元素想要看成这些数据对象。
不只空接口的Slice如此,其它包含空接口的数据结构,也都相似。