目录
1、interface{}
2、接口值的内存结构
3、接口的嵌套
没有任何方法的接口就是空接口,实际上每个类型都实现了空接口,所以空接口类型可以接受任何类型的数据。
// 定义一个空接口
type phone interface{}
// 空接口作为参数,传进来任意类型参数判断其类型与打印其值
func showmpType(q interface{}) {
fmt.Printf("type:%T,value:%vn", q, q)
}
interface{}
不包含任何方法,正因为如此,所有的类型都实现了interface{}
。interface{}
看起来好像没什么作用,但是它可用来存储任意类型的值。它有点类似C语言的void *类型。
package main
import "fmt"
func main() {
var a interface{}
var i int = 100
s := "Hello World"
// a可以存储任意类型的值
a = i
fmt.Println(a)
a = s
fmt.Println(a)
}
从概念上来讲,一个接口类型的值(简称接口值)其实有两个部分:一个具体的类型和该类型的一个值。
二者称为接口的动态类型和动态值。
在我们的概念模型中,用类型描述符来提供每个类型的具体信息,比如它的名字和方法集。对于一个接口值,类型部分就是用类型描述符来表述。接口值得内存布局如下所示:

我们首先来看一下接口的零值。在Go语言中,变量默认初始化为其零值。接口的零值就是把它的动态类型和动态值都设置为nil。
一个接口值是否为nil取决于它的动态类型,所以接口的零值是一个nil接口值,因为它的动态类型是nil。
可以通过== nil
或者 != nil
来判断接口值是否为nil。调用一个nil接口值的任何方法都会导致崩溃。
var s Shape
s.GetArea() // 崩溃:s是nil接口值
我们再来看一下接口值是某类型的一个实例的情形,我们以之前示例代码中的的Circle类型为例,当把一个Circle实例赋值给接口变量时,接口值的内存布局如下:

可以看出,接口值的动态类型是Circle类型,接口值的动态值是一个指向Circle实例的指针。
我们再来看一下接口值是某类型的实例的指针时的情形,我们还是以示例代码种的Circle类型为例,当把一个Circle实例的指针赋值给接口变量时,接口值的内存布局如下:

可以看出,接口值的动态类型是*Circle类型(注意与前面的那种情形不同),接口值的动态值仍然是一个指向Circle实例的指针。
注意:比较两个接口时,如果两个接口值的动态类型一致,但对应的动态值是不可比较的(比如slice),那么这个比较在执行时会发生崩溃:
var x interface{} = []int{1, 2, 3}
fmt.Println(x == x) // 崩溃
当处理错误或者调试时,能拿到接口值得动态类型是很有帮助的。可以使用fmt包的%T来实现这个需求:
var w io.Writer
fmt.Printf("%Tn", w) // <nil>
w = os.Stdout
fmt.Printf("%Tn", w) // *os.File
w = new(bytes.Buffer)
fmt.Printf("%Tn", w) // *bytes.Buffer
3、接口的嵌套
接口可以进行嵌套实现,通过大接口包含小接口。
// 接口类型可以进行嵌套
//定义总接口
type animal interface {
peo
dog
cat
}
//定义包含的接口
type peo interface {
}
type dog interface {
}
type cat interface {
}
主题授权提示:请在后台主题设置-主题授权-激活主题的正版授权,授权购买:
RiTheme官网 声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。