简析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语言学习笔记、语法知识、技术要点和个人理解及实战