了解go的反射
- 反射:与 面向对象中的反射类似!
- 程序运行中,获取到的信息(变量:类型,值。 结构体:字段,方法)
- 可以通过反射机制,来改变变量和值
- reflect 包, 实现反射
- reflect.Type : 类型。 指示某一种类型
- reflect.Value : 值
- reflect.Kind : 种类。用于反射场景更多,指代的是相似的一些类型,当遇到自定义类型时,使用kind获取原始种类
- 为什么使用反射
- 编写函数时,不知道函数传递的参数是什么类型。使用any接收一切参数,利用反射 reflect 识别数据类型,种类
- 用户输入数据类型不清楚时,利用反射获取数据类型,种类。 程序运行期间动态处理
静态类型和动态类型
- 在反射过程中。程序编译的时候变量类型为静态类型,程序运行过程中变量是动态类型。
- 静态类型:
- 动态类型:
- var A interface {} // 静态类型
- A = 10 // interface 静态类型, 运行时属于动态类型 int
- A = "abcd" // interface 静态类型, 运行时属于动态类型 string
反射使用
package mainimport ("fmt""reflect"
)type AType int
type User struct {name stringage intsex boolcustom AType
}// 必须是暴露出来的方法,小写的私有方法不被查看到
func (u User) Say(content string) {fmt.Printf("用户:%s 。 说:%s\n", u.name, content)
}
func (u User) GetInfo() {fmt.Printf("用户名:%s,用户年龄:%d,用户性别:%d", u.name, u.age, u.sex)
}
func analyticDataFunc(v interface{}) {// v 是一个任意变量// 1. 获取 v 的类型。 一般使用Kind,Type会被用户自定getType := reflect.TypeOf(v) // Type 类型fmt.Println("v type:", getType)// 1.1 . 通过vType 获取 v 的 名称vName := getType.Name()fmt.Println("v name:", vName)// 1.2 . 通过 vType 获取 Kind 的种类vKind := getType.Kind()fmt.Println("v kind:", vKind)// 2. 获取 v 变量中的值getValue := reflect.ValueOf(v)fmt.Println("v value:", getValue)// 2.1 遍历字段 和 具体的值:// getValue.NumField() 获取字段和值,该函数返回长度。非可遍历for index := 0; index < getValue.NumField(); index++ {vFieldName := getType.Field(index) // 字段信息vFieldValue1 := getValue.Field(index) // 值fmt.Println("v变量包含的信息:", "字段:", vFieldName, "值:", vFieldValue1)}// 2.2 遍历方法// getType.NumMethod() 获取 大写开头的 暴露的出来的函数数量!for i := 0; i < getType.NumMethod(); i++ {method := getType.Method(i)fmt.Printf("method Name:【%s】 method Type:【%v】\n", method.Name, method.Type)}}
func main() {/*反射场景- 通过反射获取信息*/var u = User{"张三", 3, true, 1000000}fmt.Println("define u data:", u)u.Say("你好")u.GetInfo()analyticDataFunc(u)
}
通过反射修改数据
package mainimport ("fmt""reflect"
)func main() {/* 通过反射修改信息 */// 通过反射修改值,需要操作对象的指针,拿到地址var num = 3.14pointer := reflect.ValueOf(&num)newValue := pointer.Elem() // 获取指针对象fmt.Println("类型:", newValue.Type()) // 使用 Kind()fmt.Println("判断该类型能否修改:", newValue.CanSet())newValue.SetFloat(2.11) // 通过反射修改数据fmt.Println("newValue:", newValue)}
通过反射修改变量的函数
package mainimport ("fmt""reflect"
)func reflectionModifiedVariableFunc(v any) reflect.Value {// 通过反射修改变量pointer := reflect.ValueOf(v) // 操作地址对象newValue := pointer.Elem()newValueType := newValue.Kind()fmt.Println(" [待修改变量] 值:", newValue)fmt.Println(" [待修改变量] 原类型:", newValueType)fmt.Println(" [待修改变量] 是否能修改:", newValue.CanSet())if newValueType == reflect.Int {newValue.SetInt(100)}if newValueType == reflect.String {newValue.SetString("李四")}return newValue
}func main() {var modifyVal1 = 1var modifyVal2 = "你好"fmt.Println("【修改前】", modifyVal1, modifyVal2)res1 := reflectionModifiedVariableFunc(&modifyVal1)res2 := reflectionModifiedVariableFunc(&modifyVal2)fmt.Println("【修改后】", res1, res2)}
通过反射修改结构体
package mainimport ("fmt""reflect"
)type Student struct {Name stringage intAge int
}func main() {/* 通过反射修改结构体变量数据 */var stu = Student{"张三", 19, 20}fmt.Println("stu:", stu)stuStructValue := reflect.ValueOf(&stu)if stuStructValue.Kind() == reflect.Ptr {// 获取指针对象newValue := stuStructValue.Elem()if newValue.CanSet() {// 找到要修改结构体的字段// 反射:字段必须大写开头 , 小写触发恐慌 panicnewValue.FieldByName("Name").SetString("lisi")newValue.FieldByName("Age").SetInt(333)}}fmt.Println("反射修改后的stu:", stu)}
反射调用结构体方法
- MethodByName 找到函数名。必须大写
- Call 执行 函数。
package mainimport ("fmt""reflect"
)type StudentA struct {Name stringAge int
}func (s StudentA) Say(content string) {/* 传入结构体方法 有参数*/fmt.Println(s.Name, "Say:【", content, "】")
}
func (s StudentA) GetInfo() {/* 传入结构体方法 无参数*/fmt.Printf("name: %s, age: %d\n", s.Name, s.Age)
}func main() {/*通过 反射修 调用方法- 1. MethodByName 找到函数名。必须大写- 2. Call 执行 函数。- 2.1 无参数 传入 nil- 2.2 有参数,传入Value类型*/var stu = StudentA{"张三", 20}fmt.Println("stu:", stu)stuStructValue := reflect.ValueOf(&stu)fmt.Printf("stuStructValue Kind:%s, Type:%s\n", stuStructValue.Kind(), stuStructValue.Type())if stuStructValue.Kind() == reflect.Ptr {// 获取指针对象newValue := stuStructValue.Elem()fmt.Printf("newValue Kind:%s, Type:%s\n", newValue.Kind(), newValue.Type())// 无参函数,通过反射调用 . 传入 nilnewValue.MethodByName("GetInfo").Call(nil)// 有参函数,通过反射调用。 传入 []Valueargs := make([]reflect.Value, 1) // make 定义参数类型args[0] = reflect.ValueOf("这是反射调用") // 必须传入 Value类型的参数 。张三 Say:【 这是反射调用 】newValue.MethodByName("Say").Call(args)}}
反射调用普通函数
package mainimport ("fmt""reflect"
)func f1() {fmt.Println("无参函数")
}
func f2(i string) {fmt.Println("有参函数:", i)}
func f3(i, s string) string {fmt.Println("有参函数,有返回值:", i, s)return s
}
func main() {/*反射调用函数*/// 无参v1 := reflect.ValueOf(f1)fmt.Println(v1.Kind(), v1.Type())v1.Call(nil)// 有参v2 := reflect.ValueOf(f2)fmt.Println(v2.Kind(), v2.Type())v2Args := make([]reflect.Value, 1)v2Args[0] = reflect.ValueOf("你好")v2.Call(v2Args)// 有参有返回值v3 := reflect.ValueOf(f3)fmt.Println(v3.Kind(), v3.Type())v3Args := make([]reflect.Value, 2)v3Args[0] = reflect.ValueOf("不好")v3Args[1] = reflect.ValueOf("aaa")v3.Call(v3Args)
}