空接口或者最小接口不包含任何方法,对实现不做任何要求。
形如:
type Any interface{}
空接口类似于Java/C#中的所有类的基类:Object 类。
可以给空接口变量赋予任何类型的值
type rectangle struct {
height float64
width float64
}
type Any interface{}
func main() {
var any Any
any = 5
fmt.Printf("%v,%[1]T\n", any)//5,int
any = "Hello,World"
fmt.Printf("%v,%[1]T\n", any)//Hello,World,string
any = &rectangle{height: 4, width: 5}
fmt.Printf("%v,%[1]T\n", any) //&{4 5},*main.rectangle
}
每个 interface{ } 变量在内存中占据两个字节:
空接口也可以配合lambda函数 (匿名函数) 进行使用:
func TypeSwitch(){
testFunc := func(any interface{}) {
switch any.(type){
case bool:
fmt.Println("The type of any is Bool")
case int:
fmt.Println("The type of any is Int")
case float32:
fmt.Println("The type of any is Float32")
case string:
fmt.Println("The type of any is string")
default:
fmt.Println("The type of any is Unknow")
}
}
testFunc("Hello,World") // The type of any is string
}
以上函数是空接口在type-switch中联合使用了lamdba函数
除了我们常用的能被搜索和排序的int数组、float数组、string数组,对于其他类型的数组我们可以通过使用空接口来实现。这里先举一个简单的例子:
type rectangle struct {
height float64
width float64
}
type Any interface{}
func main() {
var a = new([3]Any)
a[0] = "Hello,World"
a[1] = 32
a[2] = &rectangle{3,4}
for _,b := range a {
fmt.Printf("%v\t%[1]T\n",b)
}
}
其中 变量a 存储三个空接口(Any),在对每个空接口进行赋值,并进行输出,即可实现同一个数组包含不同数据类型。其输出结果如下:
Hello,World string
32 int
&{3 4} *main.rectangle
此外我们可以定义一个容器类型的结构体来包含空接口(Any)类型的元素切片。
type Container struct{
a []Any
}
其中Container里面可以存放任何类型的变量,因为空接口可以赋予任何类型的值。
我们可以定义一个方法 At 用于返回第 i 个元素的值,同时我们也可以定义一个方法 Set 用于设置第 i 个元素的值。如下:
func (p *Container) Set(i int, e Any){
p.a[i] = e
}
func (p *Container) At(i int) Any{
return p.a[i]
}
因此我们可以切片来对原数组进行修改。对之间的简例进行修改得到的完整的函数如下:
type rectangle struct {
height float64
width float64
}
type Any interface{}
type Container struct{
a []Any
}
func (p *Container) Set(i int, e Any){
p.a[i] = e
}
func (p *Container) At(i int) Any{
return p.a[i]
}
func main() {
var a = new([3]Any)
b := Container{a[:]}
a[0] = "Hello,World"
a[1] = 32
a[2] = &rectangle{3,4}
b.Set(0,"Hello")
b.Set(1,123.4)
for c := range a {
fmt.Printf("%v\t%[1]T\n",b.At(c))
}
}
其输出结果如下:
Hello string
123.4 float64
&{3 4} *main.rectangle
通过结果的对比可得已实现At、Set方法,即不仅可以修改数据的值,同时也可以修改数据的类型。
由于空接口切片的布局于一般数据切片不同,故无法使用 ‘ = ‘ 直接进行复制。编译时会出现错误。
cannot use dateSlice (type [ ] myType) as type [ ] interface { } in assignment
所以在进行复制时必须使用 for-range 一个一个地进行显式赋值,具体代码如下
dateSlice := &[...]int{1,2,3}
var interfaceSlice []Any = make([]Any,len(dateSlice))
for i,d := range dateSlice{
interfaceSlice[i] = d
}
fmt.Println(*dateSlice)
fmt.Println(interfaceSlice)
输出结果如下:
[1 2 3]
[1 2 3]
发现interfaceSlice已经复制了dateSlice的内容
该例子由空接口存储数据的一个二叉树:
type Node struct {
left *Node
data interface{}
right *Node
}
func NewNode(left,right *Node) *Node{
return &Node{left,nil,right}
}
func (n *Node) SetData(data interface{}){
n.data = data
}
func main(){
root := NewNode(nil,nil)
root.SetData("Root Node")
leftChild := NewNode(nil,nil)
leftChild.SetData("Left Node")
rightChile := NewNode(nil,nil)
rightChile.SetData("Right Node")
root.left = leftChild
root.right = rightChile
fmt.Printf("%v",root)
}
输出结果为:
&{0xc000022060 Root Node 0xc000022080}
其中0xc000022060和0xc000022080为左右孩子结点的地址。
以上程序代码均已上传到至Github ,有需要可直接进行下载