2022-6-1 实习Day49

1、清算系统排列问题后端排查,后端未接收前端排序关键字,使用排序组件不同 – 3小时 70%

2、代理行转汇退汇中退汇行swiftcode点击放大镜自动搜索前台代码修改 – 2小时 80%

3、手工分报,点击账务,必输栏位前台校验账务无字段校验组件 –2小时 100%


Golang编程学习(part 44)

1、反射的应用场景

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))
}

输出结果:

json result: {“name”:”牛魔王”,”age”:20,”sal”:588.99,”sex”:”male”}

思考问题:

为什么序列化后,key-val的key值是结构体Tag的值,而不是字段的名称,比如不是Name而是name



2、使用反射机制,编写函数的适配器,桥连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 要求如下:
// 1)定义了两个匿名函数
test1 := func(v1 int, v2 int){
t.Log(v1, v2)
}
test2 := func(v1 int, v2 int, s string){
t.Log(v1, v2, s)
}

// 2)定义一个适配器函数用作统一处理接口,其大致结构如下:
bridge := func(call interface{}, args ...interface{}){
// 内容
}

// 实现调用test1对应的函数
bridge(test1, 1, 2)
// 实现调用test2对应的函数
bridge(test2, 1, 2, "test2")

// 3)要求使用反射机制完成(学习reflect后,回头来解决)


3、反射的正式开始

【1】基本介绍
  • 反射可以在运行时动态获取变量的各种信息,比如变量的类型(type),类别(kind)

  • 如果是结构体变量,还可以获取到结构体本身的信息(包括结构体的字段、方法)

  • 通过反射,可以修改变量的值,可以调用关联的方法

  • 使用反射,需要import(”reflect”)

  • 示意图

    image-20220725011601073


【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,可以获取到关于该变量的很多信息

    image-20220725110251892

  • 变量、interface{}和reflect.Value是可以相互转换的,这点在实际开发中会经常使用到

    image-20220725110612151

    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
    32
    package 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)
    }

    image-20220725111942645



4、反射的快速入门

快速入门说明:

  • 请编写一个案例,演示对(基本数据类型、interface{}、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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package main

import (
"fmt"
"reflect"
)

type Student struct {
Name string
Age int
Sex bool
}

func main() {

reflectTest(20)
student := Student{
Name: "小张",
Age: 0,
Sex: false,
}
reflectTest2(student)
}

// 专门演示反射
func reflectTest(b interface{}) {
// 通过反射获取传入的变量的type,kind,值
// 1、先获取到reflect.Type
typeOf := reflect.TypeOf(b)
fmt.Println("type: ", typeOf)

// 2、获取到reflect.Value
valueOf := reflect.ValueOf(b)
num := valueOf.Int() + 20
fmt.Println("num: ", num)

fmt.Printf("value=%v, type=%T\n", valueOf, valueOf)
fmt.Printf("value=%v, type=%T\n", typeOf, typeOf)

// 下面我们将valueOf转成interface{}
i := valueOf.Interface()

// 将interface{}通过断言转成需要的类型
i2 := i.(int)
fmt.Println("num: ", i2)

}

// 演示对结构体的反射
func reflectTest2(b interface{}) {
// 通过反射获取传入的变量的type,kind,值
// 1、先获取到reflect.Type
typeOf := reflect.TypeOf(b)
fmt.Println("type: ", typeOf)

// 2、获取到reflect.Value
valueOf := reflect.ValueOf(b)

fmt.Printf("value=%v, type=%T\n", valueOf, valueOf)
fmt.Printf("value=%v, type=%T\n", typeOf, typeOf)

// 下面我们将valueOf转成interface{}
iv := valueOf.Interface()
fmt.Printf("value=%v type=%T\n", iv, iv)

// 将interface{}通过断言转成需要的类型
// 这里,我们就简单使用了带检测的类型断言
// 同学们可以使用switch的断言形式来做更加灵活的操作
student, ok := iv.(Student)
if ok {
fmt.Printf("student.Name=%v\n", student.Name)
}
}
1
2
3
4
5
6
7
8
9
10
type:  int
num: 40
value=20, type=reflect.Value
value=int, type=*reflect.rtype
num: 20
type: main.Student
value={小张 0 false}, type=reflect.Value
value=main.Student, type=*reflect.rtype
value={小张 0 false} type=main.Student
student.Name=小张


5、反射的注意事项和细节

【1】reflect.Value.Kind,获取变量的类别,返回的是一个常量

type Kind

type kind uint

Kind代表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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main

import (
"fmt"
"reflect"
)

func main() {
num := 20
testInt(&num)
fmt.Println("num=", num)
}

func testInt(b interface{}) {
val := reflect.ValueOf(b)
fmt.Printf("val type=%T\n", val)
fmt.Printf("val type=%T\n", reflect.TypeOf(b))
(val).Elem().SetInt(110)
fmt.Printf("val=%v\n", val)
}
1
2
3
4
val type=reflect.Value
val type=*reflect.rtype
val=0x140000aa008
num= 110

【6】reflect.Value.Elem()应该如何理解?
1
2
3
4
5
6
func main(){
var num int =100
fn := reflect.ValueOf(&num)
fn.Elem().SetInt(200)
fmt.Printf("%v\n", num)
}
1
2
3
4
// fn.Elem()用于获取指针指向变量
var num = 10
var b *int = &num
*b = 3