Go语言入门-反射
概述
定义
Reflection in computing is the ability of a program to examine its own structure, particularly through types; it’s a form of metaprogramming. It’s also a great source of confusion.
反射(Reflection )在运行时程序检查其自身结构的能力,特别是常用于类型中,能让程序在运行时检查和获取对象的类型信息和内存信息。从而实现一种类似于动态类型的功能,同时反射也是实现元编程的 形式。因为其的灵活性,也会带来一些额外的问题。
实现
go语言中反射是通过reflect包来获取程序执行过程中的反射信息。
反射建立在类型系统之上,每个变量都有具体的静态类型和对应的值,同时类型也关联了对应的接口,一个变量赋值给接口变量是接口变量也会存放变量的具体的值和变量的类型描述,因此go语言的反射与类型和接口有紧密的联系。
入口函数
go语言的中反射和其他语言反射有一定的差异,本文只探索一下go语言的反射。go语言提供了两个入口函数
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type
// ValueOf returns a new Value initialized to the concrete value
// stored in the interface i. ValueOf(nil) returns the zero Value.
func ValueOf(i interface{}) Value
以上两个函数的参数列表是 i interface{} 空接口,也就是可以传入所有类型的变量。
- 示例1-简单演示
reflect.TypeOf
和reflect.ValueOf
方法
func main() {
var a float64 = 1.2
t := reflect.TypeOf(a)
fmt.Printf("type = %v\n", t.Name())
fmt.Printf("type = %v\n", t)
v := reflect.ValueOf(a)
fmt.Printf("type = %v\n", v)
}
/**
output:
type = float64
type = float64
type = 1.2
*/
应用
reflect.TypeOf-获取类型对象
类型和种类
在反射中类型是Type,除了Type以外还增加了一个粒度的划分Kind。Type表示实际的类型,Kind表示构建Type的基础的类别(底层类型)。当我们需要某些对象是不是指针、接口体类型是,我们就可以使用Kind来判断。
- Kind一般以下几种:
// A Kind represents the specific kind of type that a Type represents. The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)
可以看出Kind本身也是一种类型,起底层的种类是unit8,而类型是Kind,Kind的范围是有限的由以上常量表示不同的类型,涵盖了go语言中的所有的内置类型。需要注意的是
Invalid Kind = iota
不是一个有效类型。可以理解为边界值。
- 示例2-简单演示Type和Kind
import (
"fmt"
"reflect"
)
type MyInt int
type MyFloat float64
type MyStruct struct {a,b int}
type MyFunc func(a, b int) error
type MyFunc2 func(a, b int) (int, error)
type MyMap map[int]string
type MyMap2 map[string]string
type MyInterface interface {hello()}
type MyInterface2 interface {}
// 别名
type MyStructAlias = MyStruct
func main() {
var myInt MyInt
var myFloat MyFloat
var myStruct MyStruct
var myFunc MyFunc
var myFunc2 MyFunc2
var myMap MyMap
var myMap2 MyMap2
var myStructAlias MyStructAlias
//var myInterface MyInterface
//var myInterface2 MyInterface2
fmt.Printf("type=[%s] kind=[%s]\n", reflect.TypeOf(myInt).Name(), reflect.TypeOf(myInt).Kind())
fmt.Printf("type=[%s] kind=[%s]\n", reflect.TypeOf(myFloat).Name(), reflect.TypeOf(myFloat).Kind())
fmt.Printf("type=[%s] kind=[%s]\n", reflect.TypeOf(myStruct).Name(), reflect.TypeOf(myStruct).Kind())
showReflectTypeAndKind(myFunc)
showReflectTypeAndKind(myFunc2)
showReflectTypeAndKind(myMap)
showReflectTypeAndKind(myMap2)
//类型别名的Type还是原来的Type
showReflectTypeAndKind(myStructAlias)
//showReflectTypeAndKind(myInterface)
//showReflectTypeAndKind(myInterface2)
}
func showReflectTypeAndKind(i interface{}) {
fmt.Printf("type=[%s] kind=[%s]\n", reflect.TypeOf(i).Name(), reflect.TypeOf(i).Kind())
}
/**
output:
type=[MyInt] kind=[int]
type=[MyFloat] kind=[float64]
type=[MyStruct] kind=[struct]
type=[MyFunc] kind=[func]
type=[MyFunc2] kind=[func]
type=[MyMap] kind=[map]
type=[MyMap2] kind=[map]
type=[MyStruct] kind=[struct]
*/
构建基础的复核类型
reflect包中提供了一下类型方法
type Type
func ArrayOf(count int, elem Type) Type
func ChanOf(dir ChanDir, t Type) Type
func FuncOf(in, out []Type, variadic bool) Type
func MapOf(key, elem Type) Type
func PtrTo(t Type) Type
func SliceOf(t Type) Type
func StructOf(fields []StructField) Type
func TypeOf(i interface{}) Type
- 示例3-反射构建复合类型
//通过反射构建数组
func initAarryByX(i interface{}, n int)(result reflect.Type){
//通过reflect.ArrayOf构建数组类型
result = reflect.ArrayOf(n, reflect.TypeOf(i))
return
}
func main() {
i := 10
//a是reflect.type类型是构建出的类型
a := initAarryByX(i, 5)
//[5]int
fmt.Println(a)
}
/**
output:
[5]int
*/
反射构建出复合、引用类型
reflect包中提供以下函数构建对象:
// New returns a Value representing a pointer to a new zero value
// for the specified type. That is, the returned Value's Type is PtrTo(typ).
func New(typ Type) Value
// NewAt returns a Value representing a pointer to a value of the
// specified type, using p as that pointer.
func NewAt(typ Type, p unsafe.Pointer) Value
- 示例4 - 构建复合类型(数组)并创建对象
//通过反射构建数组
func initAarryByX(i interface{}, n int)(result reflect.Type){
//通过reflect.ArrayOf构建数组类型
result = reflect.ArrayOf(n, reflect.TypeOf(i))
return
}
func main() {
i := 10
//a是reflect.type类型是构建出的类型
a := initAarryByX(i, 5)
//[5]int
fmt.Println(a)
//构建对象
v1 := reflect.New(a)
//
fmt.Println(v1)
}
/**
output:
[5]int
&[0 0 0 0 0]
*/
反射构建引用类型的实例
reflect包中提供以下函数对反射构建的对象初始化
// MakeSlice creates a new zero-initialized slice value
// for the specified slice type, length, and capacity.
func MakeSlice(typ Type, len, cap int) Value
// MakeChan creates a new channel with the specified type and buffer size.
func MakeChan(typ Type, buffer int) Value
// MakeMap creates a new map with the specified type.
func MakeMap(typ Type) Value
// MakeFunc returns a new function of the given Type
// that wraps the function fn. When called, that new function
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
- 示例4 简单演示 reflect.MapOf reflect.MakeMap SetMapIndex的使用
func main() {
//ty := reflect.MapOf(reflect.TypeOf("sd").(reflect.Type), reflect.TypeOf("sd").(reflect.Type))
//生成一个map类型,key的类似通过"aa"获取到类型为string value的类型通过"bb"获取的类型为string
ty := initReflectMap("aa", "bb")
//map[string]string
fmt.Println(ty)
//创建map
map1 := reflect.MakeMap(ty)
//设置map的key和value
map1.SetMapIndex(reflect.ValueOf("hello"), reflect.ValueOf("world"))
//转换构建出的类型reflect.Value到map[string]string
map2 := map1.Interface().(map[string]string)
//打印
fmt.Println(map1, reflect.TypeOf(map1))
//打印
fmt.Println(map2, reflect.TypeOf(map2))
}
/**
output:
map[string]string
map[hello:world] reflect.Value
map[hello:world] map[string]string
*/
reflect.ValueOf
reflect.ValueOf-获取原始值信息
reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值信息,主要用于对象数据值的读写。
- 示例5-通过反射获取对象值的信息
func main() {
a := 10
//a的类型为int
fmt.Printf("%T %#v\n", a, a)
//获取反射对象v v的类型为reflect.Value
v := reflect.ValueOf(a)
fmt.Printf("%T %#v\n", v, v)
//转换变量a的值以interfac{}返回
vi := v.Interface()
fmt.Printf("%T %#v\n", vi, v)
//转换变量a的值以int类型返回
vii := v.Int()
fmt.Printf("%T %#v\n", vii, vii)
//获取reflect value类型的变量种类
vk := v.Kind()
fmt.Printf("%T %#v\n", vk, vk)
//获取变量v的类型
fmt.Println(v.Type())
//类型不符合无法转换
//fmt.Println(v.Bytes())
//IsNil只用于引用类型
//fmt.Println(v.IsNil())
//判断v是否有效
fmt.Println(v.IsValid())
//判断v是否为0
fmt.Println(v.IsZero())
}
/**
int 10
reflect.Value 10
int 10
int64 10
reflect.Kind 0x2
int
true
false
*/
由于接口变量会复制对象,而ValueOf的签名是func ValueOf(i interface{}) Value。使用变量本身无法修改对象值因此需要修改指针。
reflect.ValueOf-通过反射修改对象值
- 示例6 通过反射来修改变量值
func main() {
//10 - 字面值常量地址不可达 无法获取变量的地址
a := 10
va := reflect.ValueOf(a)
fmt.Println(va.CanAddr(), va.CanSet())
//无法赋值
//va.SetInt(11)
vp := reflect.ValueOf(&a)
fmt.Println(vp.CanAddr(), vp.CanSet())
//指针本身是地址不可达, 也无法设置值的。
//vp.SetInt(11)
//但是可以通过存放a的地址副本通过a的地址来关联到a的内存地址,从而修改内存内容。
//修改a的值为11
vp.Elem().SetInt(11)
fmt.Println(a)
fmt.Println(vp.Elem().CanAddr(), vp.Elem().CanSet())
}
/**
output:
false false
false false
11
true true
*/
- 由于接口interface{}会赋值对象,并且是地址不可达,因此需要修改对象值需要传入对象指针。
- 传入的对象指针通过Elem来定位到对象实际的内存空间,从而修改对象值。
- 使用CanAddr、CanSet来判断是否可以寻址、可以设置值。
判断对象是否为空,或者有效
1. isNil
isNil的源码可以看出只要是针对引用类型的变量判空处理。如果传入非引用的变量就会panic。
// IsNil reports whether its argument v is nil. The argument must be
// a chan, func, interface, map, pointer, or slice value; if it is
// not, IsNil panics. Note that IsNil is not always equivalent to a
// regular comparison with nil in Go. For example, if v was created
// by calling ValueOf with an uninitialized interface variable i,
// i==nil will be true but v.IsNil will panic as v will be the zero
// Value.
func (v Value) IsNil() bool {
k := v.kind()
switch k {
case Chan, Func, Map, Ptr, UnsafePointer:
if v.flag&flagMethod != 0 {
return false
}
ptr := v.ptr
if v.flag&flagIndir != 0 {
ptr = *(*unsafe.Pointer)(ptr)
}
return ptr == nil
case Interface, Slice:
// Both interface and slice are nil if first word is 0.
// Both are always bigger than a word; assume flagIndir.
return *(*unsafe.Pointer)(v.ptr) == nil
}
panic(&ValueError{"reflect.Value.IsNil", v.kind()})
}
- 示例7 使用IsNil判断引用类型是否为空
func main() {
var p *string
var s []int
//var i int
//IsNil主要用来判断引用类型的变量的值是否为nil
fmt.Println(reflect.ValueOf(p).IsNil())
fmt.Println(p == nil)
fmt.Println(reflect.ValueOf(s).IsNil())
fmt.Println(s == nil)
//panic: reflect: call of reflect.Value.IsNil on int Value
//fmt.Println(reflect.ValueOf(i).IsNil())
}
/**
output:
true
true
true
true
*/
2. isValid()
//源码没看明白丫
type flag uintptr
// IsValid reports whether v represents a value.
// It returns false if v is the zero Value.
// If IsValid returns false, all other methods except String panic.
// Most functions and methods never return an invalid value.
// If one does, its documentation states the conditions explicitly.
func (v Value) IsValid() bool {
return v.flag != 0
}
- TODO
结构体反射
通过reflect.TypeOf()获得反射对象信息后,如果是struct类型,则可以通过反射对象获取方法和成员域。
- 通过以下方法成员域
// NumField returns a struct type's field count.
// It panics if the type's Kind is not Struct.
NumField() int
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type's Kind is not Struct.
FieldByIndex(index []int) StructField
// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
FieldByName(name string) (StructField, bool)
- 通过以下方法获取方法集的方法
// NumMethod returns the number of exported methods in the type's method set.
NumMethod() int
// Method returns the i'th method in the type's method set.
// It panics if i is not in the range [0, NumMethod()).
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
//
// Only exported methods are accessible and they are sorted in
// lexicographic order.
Method(int) Method
// MethodByName returns the method with that name in the type's
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method's Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method's Type field gives the
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)
- 关键的两个描述成员域和方法集的接口体
- StructField-描述成员域
type StructField struct {
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
- Method-描述方法的结构体
// Method represents a single method.
type Method struct {
// Name is the method name.
// PkgPath is the package path that qualifies a lower case (unexported)
// method name. It is empty for upper case (exported) method names.
// The combination of PkgPath and Name uniquely identifies a method
// in a method set.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
Name string
PkgPath string
Type Type // method type
Func Value // func with receiver as first argument
Index int // index for Type.Method
}
- 示例8 结构体获取成员域和方法集
import (
"fmt"
"reflect"
)
type Animal struct {
name string
Age int
}
func (a *Animal) Name() string {
return a.name
}
func (a *Animal) SetName(name string) {
a.name = name
}
func (a Animal) GetAge() int {
return a.Age
}
func main() {
animal := Animal{
name: "animal",
Age: 0,
}
ty := reflect.TypeOf(animal)
fmt.Println("==========================Field========================")
//获取结构体成员域个数
fmt.Println(ty.NumField())
//获取成员域的个数
for i := 0; i < ty.NumField(); i++ {
//通过索引去遍历成员域
f := ty.Field(i)
fmt.Println(f.Name, f.Index, f.Type, f.Tag, f.Offset)
}
f, _ := ty.FieldByName("name")
//通过FieldByName去尝试获取字段顺序
fmt.Println("FieldByName->name", f.Name, f.Index, f.Type, f.Tag, f.Offset)
f, _ = ty.FieldByName("Name")
//找不到名字为Name的域
fmt.Println("FieldByName->name", f.Name, f.Index, f.Type, f.Tag, f.Offset)
fmt.Println("==========================Method========================")
//获取方法集方法个数
fmt.Println(ty.NumMethod())
for i := 0; i < ty.NumMethod(); i++ {
//通过索引去获取方法集中的方法
m := ty.Method(i)
fmt.Println(m.Type, m.Index, m.Name, m.Func, m.PkgPath)
}
//通过方法名尝试获取方法值
m, _ := ty.MethodByName("GetAge")
fmt.Println(m.Type, m.Index, m.Name, m.Func, m.PkgPath)
//通过方法名尝试获取方法值
m, _ = ty.MethodByName("Name")
fmt.Println(m.Type, m.Index, m.Name, m.Func, m.PkgPath)
}
/**
output:
==========================Field========================
2
name [0] string 0
Age [1] int 16
FieldByName->name name [0] string 0
FieldByName->name [] <nil> 0
==========================Method========================
1
func(main.Animal) int 0 GetAge 0x4cc0f0
func(main.Animal) int 0 GetAge 0x4cc0f0
<nil> 0 <invalid reflect.Value>
*/
- 反射到方法然后进行实例方法的调用
- 示例9 通过结构体变量进行方法调用
type Animal struct {
name string
Age int
}
func (a *Animal) Name() string {
fmt.Println("getName:", a.name)
return a.name
}
func (a *Animal) SetName(name string) {
a.name = name
fmt.Println("setName")
}
func main() {
//var in []reflect.Value
animal := Animal{
name: "",
Age: 0,
}
//获取方法名
ty := reflect.TypeOf(&animal)
for i := 0; i < ty.NumMethod(); i++ {
meth := ty.Method(i)
fmt.Printf("TYPE index [%d] = [%s]\n", i, meth.Name)
}
va := reflect.ValueOf(&animal)
for i := 0; i < va.NumMethod(); i++ {
//meth := va.Method(i)
fmt.Printf("TYPE index [%d] = [%s]\n", i, ty.Method(i).Name)
}
var in []reflect.Value
//构造Name入参
va.Method(0).Call(in)
//构造setName--入参
var in1 = []reflect.Value{reflect.ValueOf("rewr")}
//通过实例值的方法进行方法调用,并修改对应的值
va.Method(1).Call(in1)
va.Method(0).Call(in)
}
/**
output:
TYPE index [0] = [Name]
TYPE index [1] = [SetName]
TYPE index [0] = [Name]
TYPE index [1] = [SetName]
getName:
setName
getName: rewr
*/
可以对结构体信息Valueof来获取方法集合,然后通过对应的索引值来获取方法对象,然后对方法对象进行.
Call
进行调用方法。
方法的参数是[]reflect.Value–切片。空参数送空切片。一个参数就是获取切片下标为0的元素。依次类推。。
需要注意的是调用方法一定是通过ValueOf而不是TypeOf
5. 反射操作tag
- 示例10 通过结构体变量进行方法调用
import (
"fmt"
"reflect"
)
type Animal struct {
name string `json:"Name" xml:"_name"`
age int `json:"Age" xml:"_age"`
}
func (a *Animal) Age() int {
return a.age
}
func (a *Animal) SetAge(age int) {
a.age = age
}
func (a *Animal) Name() string {
return a.name
}
func (a *Animal) SetName(name string) {
a.name = name
}
func NewAnimal(name string, age int) *Animal {
return &Animal{name: name, age: age}
}
func getTagFromStruct(s interface{}) {
ty := reflect.TypeOf(s)
for i := 0; i < ty.NumField(); i++ {
field := ty.Field(i)
//通过Tag标签的Get方法来获取tag-标签值 实际上就是一个字符串的处理
fmt.Printf("name: %s|tag:[%s] subtag:xml->[%s] json->[%s] othr->[%s]\n",
field.Name, field.Tag, field.Tag.Get("xml"), field.Tag.Get("json"), field.Tag.Get("othr"))
}
}
func main() {
a := NewAnimal("lisi", 10)
getTagFromStruct(*a)
}
/**
output:
name: name|tag:[json:"Name" xml:"_name"] subtag:xml->[_name] json->[Name] othr->[]
name: age|tag:[json:"Age" xml:"_age"] subtag:xml->[_age] json->[Age] othr->[]
*/
实际上获取标签是比较容易的,通过索引获取的
StructField
对象,然后通过对象StructField
的成员域获取Tag StructTag // field tag string
就能拿到描述结构体的元数据,在通过Get方法对字符串json:"Name" xml:"_name"
遍历解析即可。