反射 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序列化