interface很好用,可以存取任意类型,但我们在使用这个值类型的时候我们往往又需要知道该interface是属于哪种类型的,以便正确使用这个值。比如,如果我们提前知道该interface为int,则都是这样使用:
var a interface{} a = 5 fmt.Println(2*a.(int))
而往往我们interface可能会存放多种不同类型的数据,为了健壮,在使用时我们需要判断interface里面存放的到底为何种类型的数据,以及这些数据是否为空?比如你引用了一个类型里面的空对象是很容易引发panic的。
我们通过什么方式判断interface是否为空呢?
一个interface里面存在动态类型和动态值,只有当动态类型和动态值都为空时,该interface才为空(nil)
package main import "fmt" func main(){ var a interface{} = nil var b interface{} = (*int)(nil) fmt.Println(a==nil) // output: true fmt.Println(b==nil) // output: false }
上面例子中,因为a本身就是nil,所以在比较时结果为true不难理解。而b因为是属于动态类型为(*int),并非nil,所以在比较时返回false。
那我们一般又是通过哪种方式来判断interface是属于什么类型,以及动态值是否为空呢?这个时候我们要用上reflect反射了,如下:
package main import ( "fmt" "reflect" ) type A struct{} func main() { a := 5 getType(&a) // output: is *int var aa *A getType(aa) // output: value is nil ; is *A getType(nil) // output: in is nil } func getType(in interface{}) { if in == nil { fmt.Println("in is nil") return } if reflect.TypeOf(in).Kind() != reflect.Ptr { fmt.Println("not ptr") return } if reflect.ValueOf(in).IsNil() { fmt.Println("value is nil") } if _, ok := in.(*int); ok { fmt.Println("is *int") } if _, ok := in.(*A); ok { fmt.Println("is *A") } }
getTypereflect.TypeOf(in).Kind()reflect.ValueOf(in).IsNil()reflect.TypeOf()reflect.ValueOf()
// TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) } // ValueOf returns a new Value initialized to the concrete value // stored in the interface i. ValueOf(nil) returns the zero Value. func ValueOf(i interface{}) Value { if i == nil { return Value{} } // TODO: Maybe allow contents of a Value to live on the stack. // For now we make the contents always escape to the heap. It // makes life easier in a few places (see chanrecv/mapassign // comment below). escapes(i) return unpackEface(i) }
panic: runtime error: invalid memory address or nil pointer dereferenceIsNil()
// IsNil reports whether its argument v is nil. The argument must be // a chan, func, interface, map, pointer, or slice value; if it is // not, IsNil panics. Note that IsNil is not always equivalent to a // regular comparison with nil in Go. For example, if v was created // by calling ValueOf with an uninitialized interface variable i, // i==nil will be true but v.IsNil will panic as v will be the zero // Value. func (v Value) IsNil() bool { ... }
所以,上面的例子里面还应该加上recover来针对panic进行捕获,防止程序崩溃.