Go进阶-Go语言反射
了解反射
什么是反射?
反射(reflection)是一种在运行时检查、操作变量的类型和值的机制或能力。
为什么需要用反射?
需要反射的 2 个常见场景:
- 1.有时你需要编写一个函数,但是并不知道传给你的参数类型是什么,可能是没约定好;也可能是传入的类型很多,这些类型并不能统一表示。这时反射就会用的上了。
- 2.有时候需要根据某些条件决定调用哪个函数,比如根据用户的输入来决定。这时就需要对函数和函数的参数进行反射,在运行期间动态地执行函数。
变量有三个重要组成部分:
- Type(类型): int、string、struct、切片等
- Value(值): 具体存储的数据,例如 42、“hello”
- Kind(种类): 类型的底层分类(int、float、slice、struct、map…)
基本数据类型反射
我们一般用到的包是reflect包。
Type和Value
它提供了两种类型(或者说两个方法)让我们可以很容易的访问接口变量内容,分别是reflect.ValueOf()和reflect.TypeOf()。
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
func lookReflect(data interface{}) {
dataType := reflect.TypeOf(data) // 获取a变量的类型,返回reflect.Type类型
dataValue := reflect.ValueOf(data) // 获取a变量的值,返回reflect.Value类型
fmt.Println(data, "的Type是:", dataType, ",", data, "的Value:", dataValue)
}
func main() {
var (
a int = 100
b string = "apple"
c float64 = 3.1415
)
lookReflect(a)
lookReflect(b)
lookReflect(c)
}
/* 运行结果如下
100 的Type是: int , 100 的Value: 100
apple 的Type是: string , apple 的Value: apple
3.1415 的Type是: float64 , 3.1415 的Value: 3.1415
*/
反射的使用
反射的使用语法:
// 方法一:使用Interface()方法
realValue := value.Interface().(已知的类型)
// 方法二:直接使用转换方法比如:Int()、String()等
realValue := value.Int() // 转换成int64类型
realValue := value.String() // 转换成string类型
......
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
var (
a int = 100
b int = 200
)
aValue := reflect.ValueOf(a) // 获取a变量的值,返回reflect.Value类型
aData := aValue.Interface().(int) // 获得接口变量的真实内容再转换已知类型
bValue := reflect.ValueOf(b) // 获取b变量的值,返回reflect.Value类型
bData := bValue.Int() // 直接转换已知类型
fmt.Printf("aData的值为: %d,aData的数据类型为: %T\n" +
"bData的值为: %d,bData的数据类型为: %T\n", aData, aData, bData, bData)
}
/* 运行结果如下
aData的值为: 100,aData的数据类型为: int
bData的值为: 200,bData的数据类型为: int64
*/
补充说明:
1.转换的时候,如果转换的类型不完全符合,则直接panic,类型要求非常严格!
2.转换的时候,要区分是指针还是指
3.也就是说反射可以将“反射类型对象”再重新转换为“接口类型变量”
结构体反射
结构体的反射操作跟基本数据类型反射差不多,只需在类型断言时类型为结构体名字即可。
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
func structReflect(data interface{}) {
dataType := reflect.TypeOf(data) // 获取a变量的类型,返回reflect.Type类型
dataValue := reflect.ValueOf(data) // 获取a变量的值,返回reflect.Value类型
fmt.Println(data, "的Type是:", dataType, ",", data, "的Value:", dataValue)
// reValue转成空接口
s1 := dataValue.Interface()
// 类型断言
s, ok := s1.(Student)
if ok {
fmt.Println("学生的名字是:", s.Name, "学生的年龄是:", s.Age)
}
}
// Student 是定义的一个结构体做试验
type Student struct {
Name string
Age int
}
func main() {
// 初始化结构体值
student := Student{
Name: "张三",
Age: 18,
}
structReflect(student) // 调用方法传入赋值后结构体
}
/* 运行结果如下
{张三 18} 的Type是: main.Student , {张三 18} 的Value: {张三 18}
学生的名字是: 张三 学生的年龄是: 18
*/
反射操作
获取变量体类别
使用语法:
typeKind := reflect.TypeOf(变量名).Kind() // Type.Kind()查看
valueKind := reflect.ValueOf(变量名).Kind() // Value.Kind()查看
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
func lookKind(data interface{}) {
typeKind := reflect.TypeOf(data).Kind() // Type.Kind()查看
fmt.Println("data的类别:", typeKind)
valueKind := reflect.ValueOf(data).Kind() // Value.Kind()查看
fmt.Println("data的类别:", valueKind)
}
// Student 是定义的一个结构体做试验
type Student struct {
Name string
Age int
}
func main() {
// 初始化结构体值
student := Student{
Name: "张三",
Age: 18,
}
lookKind(student) // 调用方法传入赋值后结构体查看变量类别
}
/* 运行结果如下
data的类别: struct
data的类别: struct
*/
修改变量值
使用语法:
// 修改基本变量值
reflect.ValueOf(变量指针).Elem().SetInt(修改值)
// 修改结构体属性值
reflect.ValueOf(结构体变量指针).Elem().Field(结构体属性索引).SetString(修改值)
reflect.ValueOf(结构体变量指针).Elem().Field(结构体属性索引).SetInt(修改值)
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
func main() {
// 定义初始化变量值
var (
a int = 100
b string = "apple"
)
aValue := reflect.ValueOf(&a) // 获取a变量的值,返回reflect.Value类型,一定为指针
bValue := reflect.ValueOf(&b) // 获取b变量的值,返回reflect.Value类型,一定为指针
aValue.Elem().SetInt(40) // 修改a变量的值
bValue.Elem().SetString("banana") // 修改b变量的值
fmt.Println("a修改后的值:", a, "b修改后的值:", b)
// 修改结构体的值
// 定义和初始化结构体值
type Student struct {
Name string
Age int
}
student := Student{
Name: "张三",
Age: 20,
}
studentValue := reflect.ValueOf(&student)
// 修改结构体第一个索引变量的值
studentValue.Elem().Field(0).SetString("李四")
// 修改结构体第二个索引变量的值
studentValue.Elem().Field(1).SetInt(21)
fmt.Println("修改后的Student结构体的值:", student)
}
/* 运行结果如下
a修改后的值: 40 b修改后的值: banana
修改后的Student结构体的值: {李四 21}
*/
操作结构体属性和方法
使用语法:
// 获取结构体的字段数量
reflect.ValueOf(结构体对象).NumField()
// 结构体第几个变量的值
reflect.ValueOf(结构体对象).Field(字段索引)
// 获取结构体内部方法数量
reflect.ValueOf(结构体对象).NumMethod()
// 调用结构体内部方法,多个变量传入[]reflect.Value切片类型
dataValue.Method(方法索引).Call(变量参数)
【示例】代码如下:
package main
import (
"fmt"
"reflect"
)
type People struct { // 定义一个结构体
Name string
Age int
}
func (p People) PrintInfo() {
fmt.Println("调用了PrintInfo()方法...")
fmt.Printf("name: %s, age: %d\n", p.Name, p.Age)
}
func (p People) Add(a, b int) int {
fmt.Println("调用了Add()方法...")
return a + b
}
func main() {
// 初始化结构体
people := People{
Name: "alice",
Age: 20,
}
dataValue := reflect.ValueOf(people)
fmt.Println("结构体的字段数量:", dataValue.NumField())
for i := 0; i < dataValue.NumField(); i++ {
fmt.Printf("第%d个字段的值是: %v\n", i+1, dataValue.Field(i))
}
// 通过reflect.Value类型操作结构体内部的方法
methodNums := dataValue.NumMethod()
fmt.Println("People结构体的方法数量:", methodNums)
/* 调用的方法必须首字母大写才能有对应的反射的访问权限
方法的顺序按照ASCII的顺序排列,索引:0,1,2,3
*/
// 调用PrintInfo()方法
dataValue.Method(1).Call(nil)
// 调用Add()方法
// 由于Call()方法调用传入多个变量需要传入切片类型,所以需要定义一个切片
var params []reflect.Value
params = append(params, reflect.ValueOf(7))
params = append(params, reflect.ValueOf(9))
addNum := dataValue.Method(0).Call(params) // 返回值类型是[]reflect.Value,需要处理转型
fmt.Println("调用Add()返回的值:", addNum[0].Int())
}
/* 运行结果如下
结构体的字段数量: 2
第1个字段的值是: alice
第2个字段的值是: 20
People结构体的方法数量: 2
调用了PrintInfo()方法...
name: alice, age: 20
调用了Add()方法...
调用Add()返回的值: 16
*/