无题
2022-6-1 实习Day49
1、清算系统排列问题后端排查,后端未接收前端排序关键字,使用排序组件不同 – 3小时 70%
2、代理行转汇退汇中退汇行swiftcode点击放大镜自动搜索前台代码修改 – 2小时 80%
3、手工分报,点击账务,必输栏位前台校验账务无字段校验组件 –2小时 100%
Golang编程学习(part 44)
1、反射的应用场景
1 | package main |
输出结果:
json result: {“name”:”牛魔王”,”age”:20,”sal”:588.99,”sex”:”male”}
思考问题:
为什么序列化后,key-val的key值是结构体Tag的值,而不是字段的名称,比如不是Name而是name
2、使用反射机制,编写函数的适配器,桥连接
1 | // 要求如下: |
3、反射的正式开始
【1】基本介绍
反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)
如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)
通过反射,可以修改变量的值,可以调用关联的方法
使用反射,需要import(”reflect”)
示意图
【2】反射的应用场景
反射常见应用场景有以下两种:
不知道接口调用哪个函数,根据传入参数在运行时确定调用的具体接口,这种需要对函数或方法反射。例如以下这种桥接模式,比如我前面提出的问题。
1
2
3 // 第一个参数funcPtr以接口的形式传入函数指针,函数参数args以可变参数的形式传入,
// bridge函数中可以用反射来动态执行funcPtr函数
func bridge(funcPtr interface{}, args ...interface{})对结构体序列化时,如果结构体有指定Tag,也会使用到反射生成对应的字符串。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29 package main
import (
"encoding/json"
"fmt"
)
type Monster struct {
Name string `json:"name"`
Age int `json:"age"`
Sal float64 `json:"sal"`
Sex string `json:"sex"`
}
func main() {
monster := Monster{
Name: "牛魔王",
Age: 20,
Sal: 588.99,
Sex: "male",
}
data, err := json.Marshal(monster)
if err != nil {
fmt.Println(err)
}
fmt.Println("json result: ", string(data))
}
【3】反射重要的函数和概念
reflect.TypeOf(变量名),获取变量的类型,返回reflect.Type类型
reflect.ValueOf(变量名),获取变量的值,返回reflect.Value类型,reflect.Value是一个结构体类型。
通过reflect.Value,可以获取到关于该变量的很多信息
变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中会经常使用到
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
Sex bool
}
func main() {
student := Student{
Name: "小张",
Age: 0,
Sex: false,
}
test(student)
}
func test(i interface{}) {
value := reflect.ValueOf(i)
fmt.Printf("%T\n", value)
a := value.Interface()
fmt.Printf("%T\n", a)
student := a.(Student)
fmt.Printf("%T\n", student)
}
4、反射的快速入门
快速入门说明:
- 请编写一个案例,演示对(基本数据类型、interface{}、reflect.Value)进行反射的基本操作
- 请编写一个案例,演示对(结构体类型、interface{}、reflect.Value)进行反射的基本操作
1 | package main |
1 | type: int |
5、反射的注意事项和细节
【1】reflect.Value.Kind,获取变量的类别,返回的是一个常量
type Kind
type kind uintKind代表Type类型值表示的具体分类,零值表示非法分类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 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
Pointer
Slice
String
Struct
UnsafePointer
)
【2】Type和Kind的区别
Type是类别,Kind是类别,Type和Kind可能是相同的,也可能是不同的
比如:var num int = 10 num的Type是int,Kind也是int
比如:var stu Student stu的Type是pkg1.Student,Kind是struct
【3】通过反射可以让变量在interface{}和Reflect.Value之间相互转换,这点在前面画过示意图并在快速入门案例中讲解过
变量 <——–> interface{} <——–> reflect.Value
【4】使用反射的方式来获取变量的值(并返回对应的类型),要求数据类型匹配,比如x是int,那么就应该使用reflect.Value(x).int(),而不能使用其它的,否则报panic
func (Value) int
func (v Value) Int() int64返回v持有的有符号整数(表示为int64),如果v的Kind不是int、int8、int32、int64会报panic
【5】通过反射来修改变量,注意当使用SetXxx方法来设置需要通过对应的指针类型来完成,这样才能改变传入的变量的值,同时需要使用到reflect.Value.Elem()方法
1 | package main |
1 | val type=reflect.Value |
【6】reflect.Value.Elem()应该如何理解?
1 | func main(){ |
1 | // fn.Elem()用于获取指针指向变量 |





