Golang 反射
基本介绍
注意通过指针变量获取的变量是地址
//指针变量的kind
fmt.Printf("%v
", reflect.TypeOf(&student).Kind()) //ptr
//指针变量的 type
fmt.Printf("%T
", &student) //*main.Student
//区别于student.Name == (*student).Name
fmt.Printf("%v
", &student.Name) //0xc0000044a0
//指针变量的type
fmt.Printf("%T
", &student.Name) //*string
-
反射可以在运行时动态获取变量的各种信息, 比如变量的类型(type), 类别(kind),type和kind可能一样
type -> int,float,main.Student, *main.Student
kind - > int, float, struct, ptr
func main() { var name string = "咖啡色的羊驼" fmt.Printf("%v ", reflect.TypeOf(name)) //string fmt.Printf("%v ", reflect.TypeOf(name).Kind())//string s := stud.Student{ Name: "张三", Age: 12, } fmt.Printf("%v ", reflect.TypeOf(s))//stud.Student fmt.Printf("%v ", reflect.TypeOf(s).Kind())//struct }
-
如果是结构体变量, 还可以获取到结构体本身的信息(包括结构体的字段, 方法)
type Student struct { Name string Age int } func main() { student := Student{ Name: "张三", Age: 12, } val := reflect.ValueOf(student) fmt.Printf("%T ", val) //相当于Java的getField() name := val.FieldByName("Name") //相当于Java的get, Java中String为引用类型 fmt.Println(name.String()) age := val.FieldByName("Age") fmt.Println(age.Int()) //相当于Java的newInstance() instance := val.Interface() fmt.Println(instance) //类似Java返回的对象是Object, 需要强转 stu:= instance.(Student) fmt.Println(stu.Name) fmt.Println(stu.Age) }
-
方法和字段需要对外公开才能获取到
基本操作
-
获取类别
func main() { student := Student{ Name: "张三", Age: 12, } rVal := reflect.ValueOf(student) rTyp := reflect.TypeOf(student) //相当于Java中getTypeName() fmt.Println(rVal.Kind()) fmt.Println(rTyp.Kind()) }
-
获取字段
func main() { stu := Student{ Name: "张三", Age: 12, Gender: false, } rVal := reflect.ValueOf(stu) kind := rVal.Kind() typ := rVal.Type() if kind!= reflect.Struct { fmt.Println("error") return } //如果操作对象不是结构体就会报错 for i := 0; i < rVal.NumField(); i++ { val := rVal.Field(i) //只有type才有Tag tag := typ.Field(i).Tag.Get("json") //*(*string)(v.ptr)将ptr转为*string,然后取值 fmt.Println(val,tag) } }
-
如果修改值, 传入的必需是指针类型, 因为传入基本类型,通过值传递,函数内的对象地址不同
func reflect02(inter interface{}) { rVal := reflect.ValueOf(inter) fmt.Printf("%T ", rVal) //由于是值传递,这里对象的地址与外部的不同 fmt.Printf("%v ", rVal)//0xc00003a1f0 fmt.Printf("%v ", rVal.Kind()) //Elem()获取指针指向的对象 //rVal是地址.Elem()相当于取地址符* rVal.Elem().SetInt(100) } func main() { var num int = 12 fmt.Println(num) //0xc00000a0f8 fmt.Println(&num) reflect02(&num) fmt.Println(num) } /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ func reflect01(inter interface{}) { rVal := reflect.ValueOf(inter) fmt.Println(rVal.Kind()) //Elem方法能返回指针变量指向的值 age := rVal.Elem().FieldByName("Age") age.SetInt(19) } func main() { student := Student{ Name: "张三", Age: 12, } fmt.Println(student) //如果想调用结构体中的指针变量, 无需使用取地址符 reflect01(&student) fmt.Println(student) }
-
invoke方法
type Student struct { Name string `json:"name"` Age int `json:"age"` Gender bool `json:"gender"` } func (s Student) Add(a, b int) int { fmt.Println("invoke add") return a + b } func (s Student) Blank() { fmt.Println("blank method") } func (this *Student) SetName(name string) { this.Name = name } func main() { stu := Student{ Name: "张三", Age: 12, Gender: false, } rVal := reflect.ValueOf(stu) kind := rVal.Kind() if kind!= reflect.Struct { fmt.Println("error") return } //rVal.Method()如果通过这种方式获取到的方法,方法按照字典序排序 //同样的方法也需要公开 blank := rVal.MethodByName("Blank") blank.Call(nil) add := rVal.MethodByName("Add") //函数返回一个切片 res := add.Call(append([]reflect.Value{}, reflect.ValueOf(1), reflect.ValueOf(2))) fmt.Println(res[0]) set := rVal.MethodByName("SetName") set.Call(append([]reflect.Value{reflect.ValueOf("李四")})) fmt.Println(stu) }
例子
func main() {
stu := Student{
Name: "张三",
Age: 12,
Gender: false,
}
clazz := reflect.ValueOf(&stu)
typ := reflect.TypeOf(&stu)
if clazz.Kind() != reflect.Ptr {
fmt.Println("不是指针,不能修改值")
return
}
//这里要通过Elem()获取到地址指向的对象,才能操作
num := clazz.Elem().NumField()
for i := 0; i < num; i++ {
tag := typ.Elem().Field(i).Tag.Get("json")
if tag == "name" {
clazz.Elem().Field(i).SetString("李四")
}
}
fmt.Println(stu)
}