网站添加icp备案号甘孜热门抖音代运营
文章目录
- 一:interface{}简介
- 二、interface{}判空
- 三:注意点
- 四:实际案例
一:interface{}简介
在go中的nil只能赋值给指针、channel、func、interface、map或slice类型的变量
interface 是否根据是否包含有 method,底层实现上用两种 struct 来表示:iface 和 eface。
-
eface:表示不含method的interface结构,或者叫empty interface。对于Golang中的大部分数据类型都可以抽象出来_type结构,同时针对不同的类型还会有一些其他信息。 -
iface: 表示non-empty interface的底层实现。相比于empty interface,non-empty要包含一些method。method的具体实现存放在itab.fun变量里。
定义在 src/runtime/runtime2.go 中
type iface struct {tab *itabdata unsafe.Pointer
}type eface struct {_type *_typedata unsafe.Pointer
}
上述就是两种 interface 的定义。然后我们再看 iface中的 itab 结构:(被定义在 src/runtime/runtime2.go 中)
type itab struct {inter *interfacetype // 接口的类型_type *_type // 实际对象类型// ... 还有一些其他字段
}type _type struct {size uintptr // 大小信息.......hash uint32 // 类型信息tflag tflag align uint8 // 对齐信息.......
}
不管是iface还是eface,我们可以明确的是interface包含有一个字段_type *_type表示类型,有一个字段data unsafe.Pointer指向了这个interface代表的具体数据。
二、interface{}判空
只有内部类型都为nil,总的interface才是空的。
var inter interface{} = nil
if inter==nil{fmt.Println("empty")
}else{fmt.Println("not empty")
}
结果为 empty
nil为untyped类型,赋值给interface{},则type和value都是nil,比较的结果是true
其他有类型的赋值给interface{},结果是false`,例如
var inter interface{} = (*int)(nil)if inter==nil{fmt.Println("empty")}else{fmt.Println("not empty")}
结果为 not empty
对interface{}判空的方法是使用反射的方式进行判断
var inter interface{} = (*int)(nil)if IsNil(inter){fmt.Println("empty")}else{fmt.Println("not empty")}func IsNil(i interface{}) bool {vi := reflect.ValueOf(i)if vi.Kind() == reflect.Ptr {return vi.IsNil()}return false
}
结果为 empty
三:注意点
- 即使接口持有的值为
nil,也不意味着接口本身为nil。 - 在执行以下语句的时候,是有可能报
panic的:
var x intreflect.ValueOf(x).IsNil()
而输出也是非常明显的指出错误:
panic: reflect: call of reflect.Value.IsNil on int Value
因为不可赋值 nil 的 interface 是不能使用 reflect.Value.IsNil 方法的。
那么问题就很好解决了。
解决方式
我们在执行reflect.Value.IsNil方法之前,进行一次判断是否为指针即可:
func IsNil(x interface{}) bool {if x == nil {return true}rv := reflect.ValueOf(x)return rv.Kind() == reflect.Ptr && rv.IsNil()
}
重点在于rv.Kind() == reflect.Ptr && rv.IsNil()这段代码。
这段代码的作用:
- 判断
x的类型是否为指针。 - 判断
x的值是否真的为nil。
下面我们使用几种常见的数据类型来进行测试:
func IsNil(x interface{}) bool {if x == nil {return true}rv := reflect.ValueOf(x)return rv.Kind() == reflect.Ptr && rv.IsNil()
}func main() {fmt.Printf("int IsNil: %t\n", IsNil(returnInt())) // int IsNil: falsefmt.Printf("intPtr IsNil: %t\n", IsNil(returnIntPtr())) // intPtr IsNil: truefmt.Printf("slice IsNil: %t\n", IsNil(returnSlice())) // slice IsNil: falsefmt.Printf("map IsNil: %t\n", IsNil(returnMap())) // map IsNil: falsefmt.Printf("interface IsNil: %t\n", IsNil(returnInterface())) // interface IsNil: truefmt.Printf("structPtr IsNil: %t\n", IsNil(returnStructPtr())) // structPtr IsNil: true
}func returnInt() interface{} {var value intreturn value
}func returnIntPtr() interface{} {var value *intreturn value
}func returnSlice() interface{} {var value []stringreturn value
}func returnMap() interface{} {var value map[string]struct{}return value
}func returnInterface() interface{} {var value interface{}return value
}type People struct {Name string
}func returnStructPtr() interface{} {var value *Peoplereturn value
}
我们先后使用了 int、*int、slice、map、interface{}、自定义结构体 来测试此IsNil方法。运行程序输出为:
int IsNil: false
intPtr IsNil: true
slice IsNil: false
map IsNil: false
interface IsNil: true
structPtr IsNil: true
从测试结果来看,目前是符合我们对此方法的定位的。
四:实际案例
工作中实际的场景,一般是因为面向接口编程,可能经过了很长的链路处理,在某个节点返回了一个空的结构体,如下
某个环节有一个A方法可能会返回一个nil的*People
type People struct {Name string
}func A() *People {// ... 一些逻辑return nil
}
在调用方可能是用interface{}接收的,然后判断是否为空,用的==,发现不为nil ,后续使用该变量就会报空指针异常了
p := A()func B(people interface{}){if people != nil {fmt.Println(people.Name)}
}
如上代码便会抛panic,因为p赋值给了interface{},尽管p是nil,但是people并不是nil,因为_type不是空,但是使用people.Name会报空指针异常panic,因为data是空的。正确的做法应该是如下形式
p := A()func B(people interface{}){if !IsNil(people) {fmt.Println(people.Name)}
}func IsNil(x interface{}) bool {if x == nil {return true}rv := reflect.ValueOf(x)return rv.Kind() == reflect.Ptr && rv.IsNil()
}
