在计算机科学中,反射是指计算机程序在运行时可以访问、检测和修改它本身状态或行为的一种能力。反射实在Java出现后迅速流行起来的一种概念,通过反射可以获取丰富的类型信息,可以利用这些类型信息做比较灵活的工作。
如果不用反射也是可以的,那我们就需要用到汇编语言,与计算机的内存打交道。但我们常用的都是高级语言,则需要通过反射来实现。不同的语言实现反射的机制也是不同的,而且有些语言也不支持反射。《Go语言圣经》中是这样定义反射的:
Go语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法。但是在编译时并不直到这些变量的具体类型。这称为反射机制。
需要反射的2个常见场景:
- 不能明确函数传入的参数的类型,需要在运行时对传入的具体参数进行类型判断。
- 不确定接口调用哪些方法,需要在运行时根据传入参数确定。
反射是一把双刃剑,虽然它的功能很强大。
- 反射相关的代码,一般是难以阅读的。
- GO语言作为一门静态语言,编译器能在编码过程中提前发现一些类型错误。但是对于反射代码是无能为力的,所以包含反射相关的代码,往往运行一段时间以后才会发现错误。这往往会导致比较严重的后果。
- 反射对于性能影响是比较大的,比正常代码运行速度慢一到两个数量级。出于运行效率来讲,尽量避免使用反射。
Go语言中我们学习过interface,他是Go语言实现抽象的一个非常强大的工具。当给接口变量赋予一个实体类型的时候,接口会存储实体类型的信息。反射就是通过接口的类型信息实现的,反射建立在类型的基础之上。
Go语言在reflect包中定义了各种类型,实现了反射的各种函数。通过它们可以在运行时检测类型的信息、改变类型的值。
回顾两个知识点:
go语言是静态类型语言。在代码编译的时候,所有类型都已经确定了。
interface{}——空接口。空接口是可以代表任意类型的。
Go语言的反射是建立在类型之上的。
反射,本质上就是用来检测存储在interface{}中的pair的一种机制。pair中本质上就是存储的type和value。type和value也是反射包中最重要的两个类型。
reflect反射包提供了两种方法,reflect.TypeOf()和reflect.ValueOf()
所有的具体类型都可以看作是interface{}空接口的具体实现。
- var x interface{}
- var y string
-
- func main() {
- x = 11.11
- fmt.Println("type x:", reflect.TypeOf(x))
- fmt.Println("value x:", reflect.ValueOf(x))
-
- y = "liqi-test"
- fmt.Println("type y:", reflect.TypeOf(y))
- fmt.Println("value y:", reflect.ValueOf(y))
- }

reflect.TypeOf()返回的类型是Type,reflect.ValueOf()返回的类型是Vlue。对于Type和Vlue下,都包含了大量的方法。
- type Person struct {
- Name string
- Age int
- Sex string
- }
-
- func (p Person) Say(msg string) {
- fmt.Println("hello,", msg)
- }
-
- func (p Person) PrintInfo() {
- fmt.Printf("姓名: %s, 年龄: %d, 性别: %s\n", p.Name, p.Age, p.Sex)
- }
-
- func main() {
- p1 := Person{"xiaoming", 18, "男"}
- getMsg(p1)
- }
-
- func getMsg(msg interface{}) {
- getType := reflect.TypeOf(msg)
- fmt.Println("Type name is:", getType.Name())
- fmt.Println("Kind is:", getType.Kind())
-
- getValue := reflect.ValueOf(msg)
- fmt.Println("get all Fields:", getValue)
-
- //获取结构体字段
- fmt.Println("---获取结构体字段信息---")
- for i := 0; i < getType.NumField(); i++ {
- field := getType.Field(i)
- fieldValue := getValue.Field(i).Interface()
- fmt.Printf("结构体Field:%v, 值:%v, 值的类型: %T\n", field.Name, fieldValue, fieldValue)
- }
-
- //获取方法
- fmt.Println("---获取结构体方法信息---")
- for i := 0; i < getType.NumMethod(); i++ {
- getMethod := getType.Method(i)
- fmt.Printf("方法名称: %s, 方法类型: %s\n", getMethod.Name, getMethod.Type)
- }
- }

定义了一个Person结构体,字段有Name、Age、Sex,还定义了结构体的两个方法。
需要修改实际变量的值,也是通过reflect.ValueOf(X)获取reflect.Value对象,然后进行变量值的修改。但需要注意的是reflect.ValueOf(X)中的X必须是一个指针对象,然后使用Elem().Set()进行更改,Set()传入的数值必须是Value类型。
- func main() {
- var num float64 = 11.11
- fmt.Println("num的数值:", num)
-
- numValue := reflect.ValueOf(&num)
-
- fmt.Println("num的类型:", numValue.Elem().Type())
- fmt.Println("num是否可以修改:", numValue.Elem().CanSet())
-
- SetNum := reflect.ValueOf(22.22)
- numValue.Elem().Set(SetNum)
- fmt.Println("修改后num的数值:", num)
-
- fmt.Println("---修改结构体对象数值---")
- p1 := &Person{"xiaoming", 18, "男"}
- fmt.Println("修改前的结构体:", *p1)
- personValue := reflect.ValueOf(p1)
-
- fmt.Println("结构体对象是否可以修改?:", personValue.Elem().CanSet())
- personValue.Elem().FieldByName("Name").SetString("xiaohong")
- personValue.Elem().FieldByName("Age").SetInt(20)
- personValue.Elem().FieldByName("Sex").SetString("女")
- fmt.Println("修改后的结构体:", *p1)
- }

- type Person struct {
- Name string
- Age int
- Sex string
- }
-
- func (p Person) Say(msg string) {
- fmt.Println("hello,", msg)
- }
-
- func (p Person) PrintInfo() {
- fmt.Printf("姓名: %s, 年龄: %d, 性别: %s\n", p.Name, p.Age, p.Sex)
- }
-
- func main() {
- p1 := Person{"xiaoming", 18, "男"}
- value := reflect.ValueOf(p1)
-
- method1 := value.MethodByName("Say")
- method2 := value.MethodByName("PrintInfo")
-
- //调用方法
- args1 := []reflect.Value{reflect.ValueOf("反射执行有参数的方法")}
- method1.Call(args1) //有参数
- method2.Call(nil) //无参数
- }

函数的调用和方法类似。
- func fun1() {
- fmt.Println("反射调用无参函数")
- }
-
- func fun2(msg string) {
- fmt.Println("hello,", msg)
- }
-
- func fun3(i int, s string) (a int, b string) {
- a = i + 1
- b = s + ",从函数中return"
- return a, b
- }
-
- func main() {
- //反射调用无参数函数
- funValue1 := reflect.ValueOf(fun1)
- if funValue1.Kind() == reflect.Func {
- funValue1.Call(nil)
- }
-
- //反射调用有参数函数
- funValue2 := reflect.ValueOf(fun2)
- if funValue2.Kind() == reflect.Func {
- funValue2.Call([]reflect.Value{reflect.ValueOf("反射调用有参函数")})
- }
-
- //反射调用有返回值函数
- funValue3 := reflect.ValueOf(fun3)
- if funValue3.Kind() == reflect.Func {
- resultValue := funValue3.Call([]reflect.Value{reflect.ValueOf(10), reflect.ValueOf("函数有返回值")})
- fmt.Println("有返回值函数的返回值:", resultValue[0].Interface(), resultValue[1].Interface())
- }
- }
