Golang 进阶5—— 反射
注意,该文档只适合有编程基础的同学,这里的go教程只给出有区别的知识点
反射:
- 反射可以在运行时动态获取变量的各种信息, 比如变量的类型、 类别等信息。
- 如果是结构体变量,还可以获取结构体本身的信息(结构体的字段、方法)。
- 通过反射, 可以修改变量的值,可以调用关联的方法。
- 使用反射, 需要import(“reflect”)
1.1 main函数
package main
import (
"fmt"
"reflect"
)
// 利用一个函数, 函数的参数定义为空接口
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口
// 也可以理解为我们可以把任何一种类型赋值给空接口
func testReflect (data interface{}) {
// 1、 调用 TypeOf函数, 返回reflect.Type 类型的数据
fmt.Println("data type is ", reflect.TypeOf(data))
// 2、 调用 ValueOf函数, 返回reflect.Value 类型的数据
reVal := reflect.ValueOf(data)
fmt.Println("data value is ", reVal)
// 3、 如果要获取具体类型的值, 可以调用 Int 方法
sum := 100 + reVal.Int()
fmt.Println("sum is ", sum)
// 4、 reVal 转成空接口
i2 := reVal.Interface()
fmt.Println("i2 is ", i2)
// 5、类型断言
n := i2.(int)
n2 := n + 200
fmt.Println("n2 is ", n2)
fmt.Println("i2 type is ", reflect.TypeOf(i2))
}
1.2 输出结果
(base) PS E:\Goproject\src\gocode\testproject03> go run .\main\main.go
data type is int
data value is 10
sum is 110
i2 is 10
n2 is 210
i2 type is int
1.3 结构体情况
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
}
// 利用一个函数, 函数的参数定义为空接口
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口
// 也可以理解为我们可以把任何一种类型赋值给空接口
func testReflect (data interface{}) {
// 1、 调用 TypeOf函数, 返回reflect.Type 类型的数据
fmt.Println("data type is ", reflect.TypeOf(data))
// 2、 调用 ValueOf函数, 返回reflect.Value 类型的数据
reVal := reflect.ValueOf(data)
fmt.Println("data value is ", reVal)
// 3、 reVal 转成空接口
i2 := reVal.Interface()
fmt.Println("i2 is ", i2)
if s, ok := i2.(Student); ok {
fmt.Println("i2 is student:", s.Name)
} else {
fmt.Println("i2 is not a student")
}
}
func main () {
stu1 := Student{"xiaoxiao", 18}
testReflect(stu1)
}
1.4 输出结果
(base) PS E:\Goproject\src\gocode\testproject03> go run .\main\main.go
data type is main.Student
data value is {xiaoxiao 18}
i2 is {xiaoxiao 18}
i2 is student: xiaoxiao
1.5 获取变量类别
import (
"fmt"
"reflect"
)
type Student struct {
Name string
Age int
}
// 利用一个函数, 函数的参数定义为空接口
// 空接口没有任何方法,所以可以理解为所有类型都实现了空接口
// 也可以理解为我们可以把任何一种类型赋值给空接口
func testReflect (data interface{}) {
// 1、 调用 TypeOf函数, 返回reflect.Type 类型的数据
reType := reflect.TypeOf(data)
fmt.Println("data type is ", reType)
// 2、 调用 ValueOf函数, 返回reflect.Value 类型的数据
reVal := reflect.ValueOf(data)
fmt.Println("data value is ", reVal)
// 3、 获取变量的类别(大范围)
k1 := reVal.Kind()
fmt.Println("data kind is ", k1)
k2 := reType.Kind()
fmt.Println("data kind is ", k2)
// 4、 获取变量的类型 (小范围)
i2 := reVal.Interface()
if n, ok := i2.(Student); ok {
fmt.Println("n is ", n)
} else {
fmt.Println("n is not Student")
}
}
func main () {
stu1 := Student{"xiaoxiao", 18}
testReflect(stu1)
}
1.6 输出结果
(base) PS E:\Goproject\src\gocode\testproject03> go run .\main\main.go
data type is main.Student
data value is {xiaoxiao 18}
data kind is struct
data kind is struct
n is {xiaoxiao 18}
1.7 对结构体操作
// 1. 定义结构体
type Student struct {
Name string
Age int
}
// 2. 给结构体绑定方法
func (stu Student) Print() {
fmt.Print("调用了Print方法")
fmt.Println(stu)
}
func (stu Student) GetSum(n1, n2 int) int {
return n1 + n2
}
func (stu Student) Set(name string, age int) {
stu.Name = name
stu.Age = age
}
func testReflect(data interface{}) {
reVal := reflect.ValueOf(data)
// 检查 data 是否是指针,并获取指向的值
if reVal.Kind() == reflect.Ptr {
reVal = reVal.Elem()
}
fmt.Println(reVal)
// 获取结构体中字段的数量
n1 := reVal.NumField()
fmt.Println("字段的数量:", n1)
for i := 0; i < n1; i++ {
// 输出字段
fmt.Printf("字段 %d 的名字是 %s, 对应的值为 %v \n", i, reVal.Type().Field(i).Name, reVal.Field(i))
}
// 获取结构体的方法数量
n2 := reVal.NumMethod()
fmt.Println("方法的数量:", n2)
// 输出方法
for i := 0; i < n2; i++ {
// 输出方法
fmt.Printf("方法 %d 的名字是 %s \n", i, reVal.Type().Method(i).Name)
}
// 调用方法, 调用的方法首字母必须大写
reVal.MethodByName("Print").Call(nil)
// 调用GetSum方法
// 定义Value切片
var params []reflect.Value
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(20))
sum := reVal.MethodByName("GetSum").Call(params)
fmt.Println("sum = ", sum[0])
}
func main() {
stu := Student{Name: "Tom", Age: 18}
testReflect(stu) // 传入 stu 的指针
}
1.8 输出结果
(base) PS E:\Goproject\src\gocode\testproject03> go run .\main\main.go
{Tom 18}
字段的数量: 2
字段 0 的名字是 Name, 对应的值为 Tom
字段 1 的名字是 Age, 对应的值为 18
方法的数量: 3
方法 0 的名字是 GetSum
方法 1 的名字是 Print
方法 2 的名字是 Set
调用了Print方法{Tom 18}
sum = 30
1.9 改值
import (
"fmt"
"reflect"
)
// 1. 定义结构体
type Student struct {
Name string
Age int
}
// 2. 给结构体绑定方法
func (stu Student) Print() {
fmt.Print("调用了Print方法")
fmt.Println(stu)
}
func (stu Student) GetSum(n1, n2 int) int {
return n1 + n2
}
func (stu Student) Set(name string, age int) {
stu.Name = name
stu.Age = age
}
func testReflect(data interface{}) {
reVal := reflect.ValueOf(data)
// 通过setInt方法修改值
n := reVal.Elem().NumField()
fmt.Println("结构体中字段个数为:", n)
reVal.Elem().Field(0).SetString("Jack")
reVal.Elem().Field(1).SetInt(30)
}
func main() {
stu := Student{Name: "Tom", Age: 18}
testReflect(&stu) // 传入 stu 的指针
fmt.Println(stu)
}
1.10 输出结果
(base) PS E:\Goproject\src\gocode\testproject03> go run .\main\main.go
结构体中字段个数为: 2
{Jack 30}