一文搞懂 Go 反射之 reflect.Type

44 阅读1分钟

众所周知,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%完成

  • 我们不能通过反射来创造接口类型
  • 我们不能通过反射来申明一个新类型