非零基础自学Golang

第12章 接口与类型

12.4 空接口

12.4.1 将值保存到空接口

空接口(interface{})是Go语言中最特殊的接口。

在Java语言中,所有的类都继承自一个基类Object,而Go中的interface{}接口【空接口】就相当于Java语言里的Object。

在Go语言中,空接口不包含任何方法,也正因如此,所有的类型都实现了空接口,因此空接口可以存储任意类型的数值。

举个例子:

[ 动手写12.4.1]

package main

import "fmt"

func Log(name string, i interface{}) {
   fmt.Printf("Name = %s , Type = %T , value = %v\n", name, i, i)
}

func main() {

   var v1 interface{} = 1
   var v2 interface{} = "abc"
   var v3 interface{} = true
   var v4 interface{} = &v1
   var v5 interface{} = struct {
      Name string
   }{"Xiao Ming"}

   var v6 interface{} = &v5

   Log("v1", v1)
   Log("v2", v2)
   Log("v3", v3)
   Log("v4", v4)
   Log("v5", v5)
   Log("v6", v6)

}

运行结果

动手写12.4.1展示了空接口可以存储数字、字符串、结构体、指针等任意类型的数值,其中v4和v6指针在不同的计算机中,数值是随机的一个地址,所以会不同。

12.4.2 从空接口取值

我们接下来看如何从空接口获取值:

[ 动手写 12.4.2]

package main

import "fmt"

func main() {

   var a string = "abc"
   var i interface{} = a
   var b string = i
   fmt.Errorf(b)
}

运行结果

动手写12.4.2运行后报错,报错结果如下:

cannot use i (type interface {}) as type string in assignment: need type assertion

这个错误的意思是不能将空接口赋值到其他类型,如果需要的话必须使用类型断言。

将动手写12.4.2改为如动手写12.4.3所示,就能成功从空接口中取值。

[ 动手写 12.4.3 ]

package main

import "fmt"

func main() {

   var a string = "abc"
   var i interface{} = a
   var b string = i.(string)

   fmt.Println(b)
}

运行结果

关于类型断言,在后面再细说

12.4.3 空接口的常见使用

我们经常使用的打印函数fmt.Println就使用了空接口,Println的定义如下:

func Println(a ...interface{}) (n int, err error)

其中interface{}前面的三个点代表的是可变长参数。

可变长参数函数即其参数数量是可变的——0个或多个。声明可变参数函数的方式是在其参数类型前带上省略符(三个点)前缀,如果除了可变长参数外还有其他参数,则可变长参数必须放置在参数列表的末尾,如fmt.Printf:

func Printf(format string, a ...interface{}) (n int, err error)

打印函数支持传入任意个值,并且值的类型也是任意的。

我们可以写一个这样的例子:

[ 动手写 12.4.4]

package main

import "fmt"

func Log(args ...interface{}) {

   for num, arg := range args {
      fmt.Printf("Index => %d , Value => %v \n", num, arg)
   }
}

func main() {

   s := make([]interface{}, 3)
   s[0] = 1
   s[1] = "abc"
   s[2] = struct {
      Num int
   }{1}

   // 可变长参数
   fmt.Println("===== 将切片拆散 =====")
   Log(s...)
   fmt.Println("===== 直接传入切片 =====")
   Log(s)
}

运行结果

这里可以看见,动手写12.4.4中存在这样的用法——“s…”,这个用法的意思是将切片拆散传入到参数列表中。

在本例中,Log(s…)可以等同于Log(s[0], s[1], s[2])。