go-reflect

  1. 反射 Reflect
    1. 概念
    2. 类型Type
    3. Type和Kind
    4. 总结

反射 Reflect

概念

何为反射?在运行期,语言提供可以获取对象的类型、内存结构等信息的能力。就如同照镜子一样,通 过反射看到自身的特征。

类型Type

使用TypeOf可以获取类型信息,其签名为 func reflect.TypeOf(i any) Type 。Type是一个接口, 定义了很多方法可供调用。

reflect/type.go/Type定义

type Type interface {
    Method(int) Method // 字典序索引获取方法
    NumMethod() int // 方法的个数
    MethodByName(string) (Method, bool) //  根据名字获取方法
    Name() string // 获取类型名称
    PkgPath() string // 包路径
    Size() uintptr // 内存占用
    String() string // 类型的字符串表达
    Kind() Kind // *** 类型的种类
    
    Implements(u Type) bool // 该类型是否实现接口u
    AssignableTo(u Type) bool // 该类型是否能赋值给另一种类型u
    ConvertibleTo(u Type) bool // 该类型是否转换为另一种类型u
    
    Elem() Type // 解析指针,如果Kind不是Array、Chan、Map、Pointer、Slice则panic

    // 结构体
    Field(i int) StructField // 结构体索引i的字段
    FieldByIndex(index []int) StructField // 
    FieldByName(name string) (StructField, bool) //
    FieldByNameFunc(match func(string) bool) (StructField, bool) //
    NumField() int //
    
    Len() int // 容器的长度,如果不是Array返回panic
    Key() Type // 返回map的key类型,如果不是map返回panic
    
    // 函数
    In(i int) Type // 返回一个函数类型第i个索引的入参类型,如果不是Func或i超界则panic
    Out(i int) Type // 出参
    NumIn() int
    NumOut() int
}
  • Elem(),返回Array、Chan、Map、Pointer、Slice类型的元素的类型,因为容器类型是壳,,如同 一个盒子,如果你关心盒子里面的东西的类型,就需要Elem()。

输出如下:

[3]int, array
 3
 int
 int
 array
 map[string]int, map
 string
 func(int, int) (int, error), func
 0 int
 1 int
 0 int
 1 error

Type和Kind

从中文角度来看,Type和Kind意思接近,这里把Type翻译成类型,把Kind翻译成种类。意思很相近, 怎么区分他们呢? 从上例中,可以看出Type更具体,Kind更概括。以数组为例,Type具体指几个元素已经元素类型的数 组,Kind指数组这个大的种类,包含所有数组。 那既然有了Type类型,为什么还要Kind种类? 例如某些使用场景中,只是需要使用数组类型就行,使用Kind判断更合适、更宽松。

总结

反射的弊端

  • 代码难以阅读,难以维护
  • 编译期间不能发现类型错误,覆盖测试难度很大,有些Bug需要线上运行时才可能发现,并造成严重后果
  • 反射性能很差,通常比正常代码慢一到两个数量级。如果性能要求高时,或反复调用的代码块里建议不要使用反射

反射主要应用场合就是写库或框架,一般用不到,面试问到的概率很低

json序列化

github