简析go中接口类型变量
接口类型变量拥有两种内部表示,分别用于不同接口类型的变量:
1.eface:用于表示没有方法的空接口类型变量,即interface{}类型的变量
2.iface:用于表示拥有方法的接口类型变量
空接口类型变量eface在运行时的表示为一个非导出结构体eface,该结构体包含字段_type和data。_type为该接口类型变量的动态类型的信息,data指向一个动态分配的内存空间,该内存空间存储的是赋值给接口类型变量的动态类型变量的值。
接口类型变量iface在运行时的表示为一个非导出结构体iface,该结构体包含字段tab和data。tab存储了接口类型变量的动态类型信息、接口的类型信息、方法列表以及动态类型所实现的方法的信息。data部分则同eface中的data。
两种接口类型变量的内部表示都可以简化看做是(类型信息,数据信息)。因此,要判断两种接口类型变量是否相等,需要判断两个接口类型变量的类型信息和数据信息是否相同。都相同时两种接口类型变量才是相同的。
判断两种接口类型变量是否相同,可以借助内置函数println,println可以打印出eface或者iface两个结构体(运行时中非导出结构体)中_type及data或tab及data两个字段的值,打印结果为(_type/tab,data)。println只接收字符串类型的参数,但是在参数类型为接口类型变量时,go编译器会将println转换为能够打印出两个字段的值的函数(指针值,此处不做深入探究)。下面看示例:
type i interface {
M1()
M2()
}
type T struct {
m int
}
func (T) M1() {}
func (T) M2() {}
func main() {
var ei interface{} // 空接口类型
var ni i // 非空接口类型
println(ei) // 此处空接口类型变量ei未赋初值,为nil接口类型变量,输出(0x0,0x0),表示类型信息和数 据信息均为空
println(ni) // 此处接口类型变量ni未赋初值, 为nil接口类型变量,输出(0x0,0x0)
println(ei == ni) // (0x0,0x0) == (0x0,0x0), 结果为true
var e2 interface{} // 空接口类型
var e3 interface{} // 空接口类型
e2 = 1 // 空接口类型变量e2赋值
e3 = 2 // 空接口类型变量e3赋值
println(e2) // 输出(0xc316c0,0xc51258)
println(e3) // 输出(0xc316c0,0xc51260)
println(e2 == e3) // 输出false。
e3 = 1
println(e2 == e3) // 输出true。即只有_type/tab和data两个字段值一样的时候两个接口类型变量才相等
var ni1 i = nil // 非空接口类型, 赋值nil
var ni2 i // 非空接口类型
println(ni1) // 输出(0x231668,0x0),即非空接口类型变量类型信息不空,数据为空
println(ni1 == nil) // false。此处输出false结果是显而易见的
ni1 = T{1}
ni2 = T{2}
println(ni1) // 输出(0xaa1638,0xc000067f68)
println(ni2) // 输出(0xaa1638,0xc000067f60)
println(ni1 == ni2) // 显然,此处nil1和nil2两个非空接口类型变量的类型信息相同,但是data信息不同,输出 false
var e4 interface{} // 空接口类型
var ni3 i // 非空接口类型
e4 = T{3}
ni3 = T{3}
println(e4) // 输出(0x875ee0,0xc000067f58)
println(ni3) // 输出(0x891638,0xc000067f50)
println(e4 == ni3) // 输出true。似乎出乎意料,居然是相等的。空接口类型变量和非空接口类型变量的比较,比 较的是空接口类型变量的_type和非空接口类型变量的tab._type,但println并未输出tab._type,而是输出的tab指针的值。
}
Go语言基础及实战 文章被收录于专栏
Go语言学习笔记、语法知识、技术要点和个人理解及实战
查看11道真题和解析