反射的基本介绍:
1)反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
2)如果是结构体变量,还可以获取到结构体本身的信息(包含结构体的字段、方法)
3)通过反射,可以修改变量的值,可以调用关联的方法。
类型的相互转换:
实例1:
package main
import (
"fmt"
"reflect"
)
type student struct {
Name string
Age int
}
//修改简单数据类型
func changeData(i interface{}) {
value := reflect.ValueOf(i)
fmt.Println("kind:", value.Kind())
value.Elem().SetInt(30)
}
//修改结构体类型数据
func ModityData(i interface{}) {
value := reflect.ValueOf(i)
fmt.Println(value.Kind())
value.Elem().Field(0).SetString("江洲你好!")
value.Elem().Field(1).SetInt(33333)
}
//通过反射 改变结构体对应字段的值
func main() {
stu := student{Name: "jiangzhou", Age: 23}
fmt.Println("原数据为:", stu)
ModityData(&stu)
fmt.Println("通过反射改变数据之后的值为:", stu)
}
//通过反射改变基本数据类型的值
func main02() {
num := 10
fmt.Println("原来数据是:", num)
changeData(&num)
fmt.Println("改变之后的数据是:", num)
}
//反射的基本使用: 类型 value 以及value与数据的相互转换
func main01() {
stu := student{Name: "jiangzhou", Age: 23}
typeOf := reflect.TypeOf(stu)
fmt.Println(typeOf, typeOf.Kind())
fmt.Println("执行value操作")
value := reflect.ValueOf(stu)
fmt.Println(value)
interface1 := value.Interface()
stu1 := interface1.(student)
fmt.Println(stu1, stu1.Name, stu1.Age)
}
实例2:
package main
import (
"fmt"
"reflect"
)
/*
主要功能:1、取出结构体 tag中json对应的内容
*/
type Stu struct {
Name string `json:"name"`
Age int `json:"age"`
Address string `json:"address"`
}
func(s Stu) Print1() {
fmt.Println(s.Name,s.Age,s.Address)
}
func (s Stu)Update1 () {
s.Address="重庆"
s.Age+=1
}
func (s Stu)GetNum(n1 ,n2 int) int {
return n1+n2
}
func showTag(i interface{}) {
if reflect.TypeOf(i).Kind()!=reflect.Struct {
fmt.Println("不是结构体类型 ")
return
}
varlue:=reflect.ValueOf(i)
type1:=reflect.TypeOf(i)
num:=varlue.NumField()
for i:=0;i<num;i++{
fmt.Print("结构体第",i,"个字段为:",varlue.Field(i))
tag:=type1.Field(i).Tag //获取标签
fmt.Println("\t",tag,tag.Get("json")/*获取json对应的便签*/)
}
fmt.Println("执行方法的调用")
num1 := varlue.NumMethod() //标记:方法名一定要大写,不然会找不到对应的方法
fmt.Println("结构体方法的个数为:",num1)
varlue.Method(1).Call(nil)
args:=[]reflect.Value{reflect.ValueOf(10),reflect.ValueOf(20)}
call := varlue.Method(0).Call(args)
fmt.Println(call[0].Int())
fmt.Println("通过断言来执行的操作:")
i2 := call[0].Interface()
res,ok:=i2.(int)
fmt.Println(res,ok)
}
func main() {
stu:=Stu{Name:"jiangzhou",Age:23,Address:"cq"}
showTag(stu)
}
实例3
以下实例为测试用例(golang自带了测试框架,测试文件命名规则:XX_test.go。如以下实例的文件名称为:refelect_test.go;测试方法名称命名规则为:TestXxx,以下测试方法名称为:TestReflectFunc)
package main
import (
"testing"
)
func TestReflectFunc(t *testing.T) {
call1:= func(v1 int,v2 int) {
t.Log(v1,v2)
}
call2:= func(v1 int,v2 int,s string) {
t.Log(v1,v2,s)
}
call1(1,2)
call2(1,2,"call2")
}
通过反射完成测试方法的调用:
package main
import (
"reflect"
"testing"
)
func TestReflectFunc(t *testing.T) {
call1:= func(v1 int,v2 int) {
t.Log(v1,v2)
}
call2:= func(v1 int,v2 int,s string) {
t.Log(v1,v2,s)
}
var (
function reflect.Value
inValue []reflect.Value
n int
)
bridge:= func(call interface{},args ...interface{}) {
n=len(args)
inValue=make([]reflect.Value,n)
for i:=0;i<n;i++{
inValue[i]=reflect.ValueOf(args[i])
}
function=reflect.ValueOf(call)
function.Call(inValue)
}
bridge(call1,1,2)
bridge(call2,1,2,"call2")
}
不知道接口 调用那个 函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。如以上这种桥接模式:
bridge:= func(call interface{},args ...interface{}){...}
第一个参数call以接口的形式传入函数指针,函数参数args以可变参数的形式传入,bridge函数中可以用反射来动态执行call函数。
实例4
package main
import (
"encoding/json"
"fmt"
)
type Stu1 struct {
Name string `json:"stuName"`
Age int `json:"stuAge`
Address string `json:"StuAddress"`
}
func main() {
stu:=Stu1{Name:"jiangzhou",Age:23,Address:"cq"}
data,_:=json.Marshal(stu)
fmt.Println(string(data))
}
对结构体序列化时,如果结构体有指定Tag,goland底层也会使用到反射生成对应的字符串。