反射介绍:
反射是在运行时,动态的获取变量的各种信息,如变量的类型,类别等信息
可以获得结构体的信息(字段,方法)
通过反射,可以修改变量的值,调用关联的方法
使用反射需要导入reflect包
反射弥补了静态语言上的不足
反射是实现元编程的重要手段
在反射中,变量,空接口,reflect.Value是可以相互转换的
反射应用点
结构体的tag
自己可以写go框架的时候,函数的适配器
变量-空接口-reflect.Value(Type)
package main
import (
"fmt"
"reflect")
func reflectTest(b interface{}) {
//得到一个reflect.Type类型
rType := reflect.TypeOf(b)
fmt.Println(rType)
//得到一个reflect.Value类型
rVal := reflect.ValueOf(b)
fmt.Println(rVal)
fmt.Printf("%T\n", rVal)
//reflect.Value类型转换成空接口
iV := rVal.Interface()
var num2 = iV.(int)
fmt.Println(num2)
}
func main() {
var num int = 100
reflectTest(num)
}
类型
一个简单的基本类型反射判断
package main
import (
"fmt"
"reflect")
func main() {
var (
a = 100
)
t := reflect.TypeOf(a)
fmt.Println(t)
}
输出
int
打印出了变量a的数据类型
对于自定义类型的类型反射判断
package main
import (
"fmt"
"reflect")
type sim int
func main() {
var a sim = 100
t := reflect.TypeOf(a)
//type是判断类型(静态类型),kind是判断基础结构(底层类型)
fmt.Println(t.Name(), t.Kind())
}
输出:
sim int
对于指针
package main
import (
"fmt"
"reflect")
func main() {
a := 10
t1, t2 := reflect.TypeOf(a), reflect.TypeOf(&a)
fmt.Println(t1, t2)
fmt.Println(t1 == t2.Elem())
}
输出:
int *int
true
方法Elem返回指针,数据,切片,字典或通道的基类型。
所以这里的判断是true
拓展:
func main() {
fmt.Println(reflect.TypeOf(map[string]int()).Elem())
fmt.Println(reflect.TypeOf([]int32{}).Elem())
}
输出:
int
int32
得到的都是他们的基类型
对于结构体
package main
import (
"fmt"
"reflect")
type student struct {
name string
age int
}
func main() {
var simple student
t := reflect.TypeOf(&simple)
//fmt.Println(t) 获取的结构体指针
//fmt.Println(reflect.Ptr)
//fmt.Println(t.Kind()) //判断是不是指针类型
if t.Kind() == reflect.Ptr {
t = t.Elem()
//fmt.Println(t)取得基类,是一个结构体main.student
}
//遍历结构体
for i := 0; i < t.NumField(); i++ {
f := t.Field(i)
fmt.Println(f.Name, f.Type, f.Offset)
if f.Anonymous {
for x := 0; x < f.Type.NumField(); x++ {
af := f.Type.Field(x)
fmt.Println(" ", af.Name, af.Type)
}
}
}
}
输出:
name string 0
age int 16
值
取得变量的值
type获取类型,value可以获取值。
package main
import (
"fmt"
"reflect")
func main() {
a := "simple"
t := reflect.ValueOf(a)
fmt.Println(t)
}
方法
package main
import (
"fmt"
"reflect")
type X struct{}
func (X) Test(x, y int) (int, error) {
return x + y, fmt.Errorf("err:%d", x+y)
}
func main() {
var a X
v := reflect.ValueOf(&a)
m := v.MethodByName("Test")
in := []reflect.Value{
reflect.ValueOf(1),
reflect.ValueOf(2),
}
out := m.Call(in)
for _, v := range out {
fmt.Println(v)
}
}
3
err:3
结构体:
package main
import (
"fmt"
"reflect")
type Student struct {
name string
age int
}
func reflectTest1(b interface{}) {
//得到一个reflect.Type类型
rType := reflect.TypeOf(b)
fmt.Println(rType)
//得到一个reflect.Value类型
rVal := reflect.ValueOf(b)
fmt.Println(rVal)
fmt.Printf("%T\n", rVal)
// reflect.Value类型变成空接口
iV := rVal.Interface()
fmt.Printf("%v,%T \n", iV, iV)
// 通过断言转换成需要的类型
stu, ok := iV.(Student)
if ok {
fmt.Println("stu.name:", stu.name)
}
}
func main() {
stu := Student{
"simple",
10,
}
reflectTest1(stu)
}
反射修改变量值
package main
import (
"fmt"
"reflect")
func reflect1(b interface{}) {
rVal := reflect.ValueOf(b)
//fmt.Println(rVal.Kind()) 类别是一个指针
rVal1 := rVal.Elem()//取指针指向的值
rVal1.SetInt(20)
}
func main() {
num := 100
reflect1(&num)
fmt.Println(num)
}
输出
20
package main
import (
"fmt"
"reflect")
func main() {
name := "simple"
rVal := reflect.ValueOf(&name)
rVal.Elem().SetString("jack")
fmt.Println(name)
}
输出:
jack
反射操作结构体
Method
func (Value) [Method]
func (v [Value]) Method(i int) Value
返回v持有值类型的第i个方法的已绑定(到v的持有值的)状态的函数形式的Value封装。返回值调用Call方法时不应包含接收者;返回值持有的函数总是使用v的持有者作为接收者(即第一个参数)。如果i出界,或者v的持有值是接口类型的零值(nil),会panic。
Call
func (Value) Call
func (v Value) Call(in [][Value]) [][Value]
Call方法使用输入的参数in调用v持有的函数。例如,如果len(in) == 3,v.Call(in)代表调用v(in[0], in[1], in[2])
(其中Value值表示其持有值)。如果v的Kind不是Func会panic。它返回函数所有输出结果的Value封装的切片。和go代码一样,每一个输入实参的持有值都必须可以直接赋值给函数对应输入参数的类型。如果v持有值是可变参数函数,Call方法会自行创建一个代表可变参数的切片,将对应可变参数的值都拷贝到里面。
package main
import (
"fmt"
"reflect")
// 定义结构体
type Monster struct {
Name string `json:"name"`
age int `json:"age"`
Score float32
sex string
}
// Print方法
func (s Monster) Print() {
fmt.Println("start")
fmt.Println(s)
fmt.Println("end")
}
// 和方法
func (s Monster) GetSum(n1, n2 int) int {
return n1 + n2
}
// Set方法
func (s Monster) Set(name string, age int, score float32, sex string) {
s.Name = name
s.age = age
s.Score = score
s.sex = sex
}
func testStruct(a interface{}) {
//获取reflect.Type和reflect.Value
typ := reflect.TypeOf(a)
val := reflect.ValueOf(a)
//看看类别
kd := val.Kind()
//看看是不是结构体
if kd != reflect.Struct {
fmt.Println("不是结构体")
return
}
//获取字段数量
num := val.NumField()
fmt.Printf("struct has %d fields\n", num)
//遍历字段
for i := 0; i < num; i++ {
fmt.Printf("Field %d : 值为:%v\n", i, val.Field(i))
//获取tag
tagVal := typ.Field(i).Tag.Get("json")
//判断是否有tag,有就输出
if tagVal != "" {
fmt.Printf("Field %d: tag为:%v\n", i, tagVal)
}
}
//获取方法数量
numofMethod := val.NumMethod()
fmt.Printf("结构体有%d个方法\n", numofMethod)
//调用第二个函数,第二个函数没有参数
val.Method(1).Call(nil)
//反射的函数排序是按照函数名的ascii码来排序的,G,P,S来排序的,所以这里的第二个就是Print
//准备函数的参数,参数要是[]reflect.Value切片类型的
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(40))
//一个10,一个是40
//调用函数,并传入参数
res := val.Method(0).Call(params)
//res还是一个切片,返回的是一个切片
fmt.Println(res)
fmt.Println("res= ", res[0].Int())
}
func main() {
//定义一个实例
a := Monster{
"simple",
20,
30.1,
"难",
}
//进入反射
testStruct(a)
}
输出:
struct has 4 fields
Field 0 : 值为:simple
Field 0: tag为:name
Field 1 : 值为:20
Field 1: tag为:age
Field 2 : 值为:30.1
Field 3 : 值为:难
结构体有3个方法
start
{simple 20 30.1 难}
end
50
res= 50