1. 常见使用问题
1.1 var _ I = (*T)(nil) 是什么意思?
- 作用:用简单的语法,检查T这个struct是否实现了I这个接口
- 细化理解:可以把=左右两边分开来看
- 左边:
var _ I
等价于我们平时用的 var variable type
- 右边:
(* T)(nil)
等价于 var variable *T nil
- 示例代码:
package main
import "fmt"
type I interface {
}
type I2 interface {
say()
}
type TestStruct struct{}
func main() {
var _ I = (*TestStruct)(nil)
var testStruct *TestStruct = nil
var i I
i = testStruct
fmt.Println(i)
}
1.2 golang 结构体和指针实现接口
- 当初始化为结构体指针的时候,不管实现方法的接受者是指针还是结构体都可以调用
package main
import "fmt"
type Duck interface {
Quack()
}
type Cat struct{}
func (c Cat) Quack() {
fmt.Println("meow")
}
func main() {
var c Duck = &Cat{}
c.Quack()
}
- 当初始化为结构体的时候,实现方法的接受者为结构体的时候可以,实现方法的接受者为指针则不行
package main
import "fmt"
type Duck interface {
Quack()
}
type Cat struct{}
func (c *Cat) Quack() {
fmt.Println("meow")
}
func main() {
var c Duck = Cat{}
c.Quack()
}
- 总结如图:
- 原因分析:
- 首先我们先排除,结构体初始化变量,结构体实现接口 以及 结构体指针初始化变量,结构体指针初始化变量,因为这两个都一一对应,就很好理解
- 其次我们分析,为什么结构体指针初始化变量,结构体实现接口可以进行调用?
- 最后分析,为什么结构体初始化变量,指针实现接口不可以进行调用?
- 因为反过来结构体并不能反向获取到那个实现了接口的指针
- 简单总结就是(自己总结的):可以每一个指针对象都可以知道自己所属的类型。但是不是每一个类型,他都能知道是这个类型的所有指针。换到现实生活里面,一个班上的同学(指针)都知道自己是喜欢班花A或班草B(结构体),但是 班花A或班草B 并不知道具体有哪些同学喜欢自己.....
- 官方一点就是:
- 在golang中所有的参数传递都是值传递
- 所以当接受者是*Cat的时候,调用者如果是结构体,我们是没办法从结构体推出来一个指针,编译器不会凭空创造一个指针,所以失败
- 当接受者是Cat的时候,调用者如果是指针,那么我们是可以从指针反解出来类型,相当于就变成了调用者是结构体,接受者也是结构体
1.3 所有的 nil 都是相等的吗?
- 结论是不会。我们从以下代码可以看出,t1和s1都是nil,但是他们两个不同类型 无法直接进行比较,也就是说nil本身是包含了类型信息了,否则两个nil相比较的时候不会提示类型不一致
package main
import "fmt"
type TestStruct struct{}
func main() {
var t1 *TestStruct
var s1 *string
fmt.Println(t1)
fmt.Println(s1)
}
- **经过搜索我们可以定位到builtin/builtin.go,证明nil他是一个变量,这个变量类型可以是pointer, channel, func, interface, map, or slice **
var nil Type
1.4 空的interface一定是nil吗?
- 以下代码输出true和false,第二个false证明了,哪怕打印出来时nil的interface也不一定就==nil
- 要分析这个问题,首先我们要知道两点:
- 第一点是nil底层是什么?**参考 上面1.3 **
- 第二点是interface底层是什么结构呢?
- 首先要知道,一个interface{}类型的变量包含了两个指针,一个指向值的类型,另外一个指向实际的值
- 其次,我们分析下面,我们从*TestStruct到interface{}其实是有发生类型转换的。
- 然后,我们反着推,我们是可以从通过NilOrNot的传入参数,反射后得到传入参数的类型以及数据值的,也就是说经过类型转换后的值依旧保留着原有数据的类型以及值
- 最后,就下面的例子,经过数据转换后,经过传入参数的值依旧为空,但是指向的类型并不是,所以的话就不等于nil了
package main
import "fmt"
type TestStruct struct{}
func NilOrNot(v interface{}) bool {
fmt.Println(fmt.Sprintf("%v,%T",v,v))
return v == nil
}
func main() {
var s *TestStruct
fmt.Println(s == nil)
fmt.Println(fmt.Sprintf("%v,%T",s,s))
fmt.Println(NilOrNot(s))
}
2. interface 底层实现
2.1 interface 底层组成有哪几种?
- interface分类:分为两种
iface
和 eface
- 源码地址:runtime/runtime2.go
- 备注:以下分析我就直接在代码里面中文注解各个字段大概意思,目前也没有实战接触到这块(可能后面细化深入写反射的时候会用到?),所以就不细讲了,想深入了解的可以网上搜一下其他的
2.3 iface
type iface struct {
tab *itab
data unsafe.Pointer
}
type itab struct {
inter *interfacetype
_type *_type
hash uint32
_ [4]byte
fun [1]uintptr
}
type _type struct {
size uintptr
ptrdata uintptr
hash uint32
tflag tflag
align uint8
fieldAlign uint8
kind uint8
equal func(unsafe.Pointer, unsafe.Pointer) bool
gcdata *byte
str nameOff
ptrToThis typeOff
}
type arraytype struct {
typ _type
elem *_type
slice *_type
len uintptr
}
type functype struct {
typ _type
inCount uint16
outCount uint16
}
2.2 eface
- 什么是eface:没有含有方法的接口
- 实现代码如下:
type eface struct {
_type *_type
data unsafe.Pointer
}
3. 参考链接