众所周知,Go 是一门静态语言,我们无法在运行时动态改变代码的行为,但是 Go 提供了反射的能力,能让我们在运行时动态地获取自身的信息,在 Go 中,我们可以对任何非接口类型的值调用 reflect.TypeOf 来获得 reflect.Type,这时候获得是非接口类型的值,如果我们对接口类型进行调用,那我们就会获得接口的动态类型,reflect.Type 其实是一个接口类型,我们可以对其进行调用来获取具体的信息,但是并不是所有方法都适用
func main() {
type A = [16]int16
var c <-chan map[A][]byte
tc := reflect.TypeOf(c)
fmt.Println(tc.Kind()) // chan
fmt.Println(tc.ChanDir()) // <-chan
tm := tc.Elem()
ta, tb := tm.Key(), tm.Elem()
fmt.Println(tm.Kind(), ta.Kind(), tb.Kind()) // map array slice
tx, ty := ta.Elem(), tb.Elem()
fmt.Println(tx.Kind(), ty.Kind()) // int16 uint8
fmt.Println(tx.Bits(), ty.Bits()) // 16 8
fmt.Println(tx.ConvertibleTo(ty)) // true
fmt.Println(tb.ConvertibleTo(ta)) // false
fmt.Println(tb.Comparable()) // false
fmt.Println(tm.Comparable()) // false
fmt.Println(ta.Comparable()) // true
fmt.Println(tc.Comparable()) // true
}
类似的,我们可以通过反射获取类型拥有的字段、方法,还可以获取 tag
type T struct {
X int `max:"99" min:"0" default:"0"`
Y, Z bool `optional:"yes"`
}
func main() {
t := reflect.TypeOf(T{})
x := t.Field(0).Tag
y := t.Field(1).Tag
z := t.Field(2).Tag
fmt.Println(reflect.TypeOf(x)) // reflect.StructTag
// v is a string
v, present := x.Lookup("max")
fmt.Println(len(v), present) // 2 true
fmt.Println(x.Get("max")) // 99
fmt.Println(x.Lookup("optional")) // false
fmt.Println(y.Lookup("optional")) // yes true
fmt.Println(z.Lookup("optional")) // yes true
}
当然,反射包中还提供了很多其他的方法
func main() {
ta := reflect.ArrayOf(5, reflect.TypeOf(123))
fmt.Println(ta) // [5]int
tc := reflect.ChanOf(reflect.SendDir, ta)
fmt.Println(tc) // chan<- [5]int
tp := reflect.PtrTo(ta)
fmt.Println(tp) // *[5]int
ts := reflect.SliceOf(tp)
fmt.Println(ts) // []*[5]int
tm := reflect.MapOf(ta, tc)
fmt.Println(tm) // map[[5]int]chan<- [5]int
tf := reflect.FuncOf([]reflect.Type{ta},
[]reflect.Type{tp, tc}, false)
fmt.Println(tf) // func([5]int) (*[5]int, chan<- [5]int)
tt := reflect.StructOf([]reflect.StructField{
{Name: "Age", Type: reflect.TypeOf("abc")},
})
fmt.Println(tt) // struct { Age string }
fmt.Println(tt.NumField()) // 1
}
反射最开始的设计目标是让所有的操作都能通过反射来完成,但是这个目标并没有 100%完成
- 我们不能通过反射来创造接口类型
- 我们不能通过反射来申明一个新类型