前面已经接触过断言了,它的写法有两种形式:
t := i.(T) // 1
t, ok := i.(T) // 2
以上是希望把 i 转换成 T 类型的对象,第一种写法如果转换失败,会触发系统的 panic,导致程序退出;第二种写法如果断言失败,ok 被设置成 false,而 t 被设置成零值。
特殊情况是如果 i 是 nil 也会断言失败,哪怕是转成 interface{} 也不行。
var i interface{} // 默认是 nil 值
_ = i.(interface{}) // panic
_ = i.(string) // panic: interface conversion: interface {} is nil, not string
如果能知道所有预知的类型就不需要断言了,处理起来也会更加高效、可靠,如何得到类型呢?
func caseType(what interface{}) {
switch what.(type) {
case int:
what = what.(int) + 1
fmt.Printf("number %d\n", what)
case string:
fmt.Printf("string %s\n", what.(string)+" suffix")
case nil:
fmt.Println("you are nothing")
}
}
使用 i.(type) 获取了 i 的类型,这个语法结构叫 type-switch 断言,也就是 i.(type) 只能用在 switch 中,要直接获取类型也只能使用反射来实现,下面的代码实现和上面 type-switch 相同的功能。
func detectType(what interface{}) {
val := reflect.ValueOf(what)
t := val.Type() // Type 是接口对象
fmt.Printf("%T\n", t) // *reflect.rtype
//fmt.Println(t.Name()) // 类型名称
if val.Kind() == reflect.Int {
number := val.Int() + 1
fmt.Printf("number %d\n", number) // number 11
} else if val.Kind() == reflect.String {
fmt.Printf("string %s\n", val.String()+" suffix") // string test suffix
}
}
reflect.ValueOf(what) 获取对象的运行时信息,val.Type 获取对象的类型描述信息(类型、字段、名称、大小、对齐等),它是一个 Type 接口对象 type Type interface {…},val.Kind() 获取反射的数据类型,它其实是 type Kind uint {…} 的定义,这些类型在 go 源文件 src/reflect/type.go 中定义,包括 Bool、Int、Uint、Float、Complex、Array、Chan、Func、Interface、Map、Ptr、Slice、String、Struct、UnsafePointer,通过对每一种类型的特殊处理,就能完全的动态的去处理一个对象。
什么是特殊处理,以指针对象为例,如果 what 是一个指针对象,那么 reflect.ValueOf(what) 的结果是 reflect.Ptr,要获取它指向的对象需要继续调用 val.Elem() 函数,可以提取出这样一个函数
func getReflectValue(x interface{}) reflect.Value {
val := reflect.ValueOf(x)
// Uintptr
// UnsafePointer
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
return val
}
对于 int、string、float 等简单的值可以直接获取,但是对于 reflect.Array 和 reflect.Slice 就需要遍历它的长度 val.Len(),使用 val.Index(index).Interface() 来获取它的值,至于它的元素是什么值呢? 对了,还是判断它的类型,所以要完成一个对象的反射遍历,必然是一个递归函数。
对 reflect.Struct 的处理也很特殊,它的字段数由 val.NumField() 函数获取,字段由 val.Field(index) 来获取,同样的对 reflect.Map 需要使用 val.MapKeys() 获取键集合,val.MapIndex(key) 来获取键对应的值。
总结一下大概需要分 5 种情况处理:
1. 数值型的 reflect.Int、reflect.Uint、reflect.Float、reflect.Complex
2. 布尔和字符串 reflect.Bool、reflect.String
3. 集合 reflect.Array、reflect.Slice
4. 键值对 reflect.Map
5. 特殊的 reflect.Ptr、refelect.Interface、reflect.Chan、reflect.Func
func reflectIterate(x interface{}, handler func(val int)) {
val := getReflectValue(x)
switch val.Kind() {
case reflect.Int, reflect.Uint, reflect.Float32, reflect.Complex64:
// 其他不同位数的数值类型
// 以简单值调用 handler
case reflect.Bool:
// 以简单值调用 handler
case reflect.String:
// 以简单值调用 handler
case reflect.Interface:
// 特殊的
case reflect.Chan:
// 特殊的
case reflect.Func:
// 特殊的
case reflect.Struct:
for i := 0; i < val.NumField(); i++ {
reflectIterate(val.Field(i).Interface(), handler)
}
case reflect.Slice, reflect.Array:
for i := 0; i < val.Len(); i++ {
reflectIterate(val.Index(i).Interface(), handler)
}
case reflect.Map:
for _, key := range val.MapKeys() {
reflectIterate(val.MapIndex(key).Interface(), handler)
}
}
}
本章节的代码