Go 语言入门很简单:Go 反射
Go 语言也提供了 Java 类似的功能:反射。在编译不知道类型的情况下做到动态地操纵对象的值:
-
更新变量
-
运行时查看值
-
调用方法
什么是反射
Go 语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。 -- 《Go 语言圣经》
反射是指在程序运行期对程序本身进行访问和修改的能力,程序在编译时变量被转换为内存地址,变量名不会被编译器写入到可执行部分,在运行程序时程序无法获取自身的信息。
反射可以在程序编译期将变量的反射信息,如字段名称、类型信息、结构体信息等整合到可执行文件中,并给程序提供接口访问反射信息,这样就可以在程序运行期获取类型的反射信息,并且有能力修改它们。
reflect 包
Go语言中的反射是由 reflect 包提供支持的,reflect 包提供了 reflect.TypeOf 和 reflect.ValueOf 两个函数来获取任意对象的 Value 和 Type。
reflect 包实现了运行时的反射能力,能够让程序操作不同类型的对象。反射包中有两对非常重要的函数和类型,两个函数分别是:
-
reflect.TypeOf 能获取类型信息;
-
reflect.ValueOf 能获取数据的运行时表示;
func reflect.ValueOf(interface interface{}) Value
获取类型
Go 使用 reflect.TypeOf()
函数可以获得任意值的类型对象(reflect.Type)
package main
import (
"fmt"
"reflect"
)
func main() {
hello := "Hello,World!"
typeOfHello := reflect.TypeOf(hello)
fmt.Println(typeOfHello.Name(), typeOfHello.Kind())
}
运行该代码:
λ go run reflect_demo.go
string string
代码解释:
-
第 5 行导入
reflect
包 -
第 10 行定义了一个 “Hello,World!”的字符串变量 hello
-
第 12 行定义一个 typeOfHello 的变量接收 hello 的类型
-
第 14 行打印出 typeOfHello 变量的类型名为 string,类别(Kind)为 string。
(Type)和类别(Kind)的区别:Type 表示 Go 语言的一个类型,它是一个有很多方法的接口,可以用来识别一个结构的字段或者一个函数的各个参数。但在反射中,当需要区分一个大品种的类型时,就会用到种类(Kind)。例如需要统一判断类型中的指针时,使用类别(Kind)信息就较为方便。
reflect.Type 和 reflect.Value
reflect.TypeOf 函数接受任何的 interface{} 参数,并且把接口中的动态类型以 reflect.Type 形式返回,它返回的总是具体类型(而不会是接口类型)。
reflect.Value 可以包含一个任意类型的值。可以返回一个接口值。
reflect.ValueOf 函数接收任意的 interface{} 并将接口的动态值以 reflect.Value 的形式返回。同样也是返回具体值。
package main
import (
"fmt"
"reflect"
)
func main() {
x := []int{21, 22}
var y reflect.Value = reflect.ValueOf(&x)
y = y.Elem()
fmt.Println("The Slice is:", x)
y = reflect.Append(y, reflect.ValueOf(2021))
fmt.Println("追加元素的 Slice为:", y)
}
运行代码:
λ go run main.go
The Slice is: [21 22]
追加元素的 Slice为: [21 22 2021]
通过反射修改值
反射也可以用来修改变量的值,那么一定要传入变量的指针类型,这样才能修改原来的值。同时需要使用到reflect.ValueOf(v).Elem()
方法。
Elem 返回 v 持有的接口保管的值的 Value 封装,或者 v 持有的指针指向的值的 Value 封装。如果 v 的 Kind 不是 Interface 或 Ptr 会 panic ;如果 v 持有的值为 nil ,会返回 Value 零值。
package main
import (
"fmt"
"reflect"
)
// 使用interface{}接收任意类型的反射
func transType(v interface{}) {
rVal := reflect.ValueOf(v) // 将interface{}转成reflect.Value类型
rVal.Elem().SetInt(2023)
}
func main() {
var num = 1999
transType(&num)
fmt.Println(num)
}
运行代码:
λ go run main.go
2023
在数组上使用
也可以在数组的整数上使用反射 Append 和 ValueOF 函数:
package main
import (
"fmt"
"reflect"
)
func main() {
origin := reflect.ValueOf([]int{11, 12, 19})
result := reflect.ValueOf([]int{13, 21, 31})
fmt.Println("The value after using reflect is", origin, result)
}
运行代码,结果为:
λ go run main.go
The value after using reflect is [11 12 19] [13 21 31]
总结
在 Golang 反射中,反射允许我们在动态处更改或修改对象或任何变量。Go 作为静态类型语言,需要利用 Go 语言的 reflect 包反映来使用此功能。 可以通过 reflect.Type 获得任意变量类型,通过 reflect.value 获取或者设置实际变量的值。
参考资料:
- 点赞
- 收藏
- 关注作者
评论(0)