反射用途
反射在许多方面都非常有用,比如:
动态编程: 通过反射,你可以动态地创建对象,调用方法,甚至构建全新的类型。
框架与库开发: 很多流行的Go框架,如Gin、Beego等,都在内部使用反射来实现灵活和高度可定制的功能。
元编程: 你可以写出可以自我分析和自我修改的代码,这在配置管理、依赖注入等场景中尤为有用。
作者:techlead_krischang
链接:https://juejin.cn/post/7291094611484966970
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
反射与类型系统
反射紧密地与类型系统联系在一起。在静态类型语言(例如Go)中,每一个变量都有预先定义的类型,这些类型在编译期就确定。但反射允许你在运行时去查询和改变这些类型。
// 代码示例:查询变量的类型和值
import "reflect"
func main() {
var x int = 42
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println("Type:", t)
fmt.Println("Value:", v)
}
// 输出: Type: int
// 输出: Value: 42
反射与接口
在Go中,接口(interface)和反射是紧密相关的。事实上,你可以认为接口是实现反射的“入口”。当你将一个具体类型的变量赋给一个接口变量时,这个接口变量内部存储了这个具体变量的类型信息和数据。
// 代码示例:接口与反射
type Any interface{}
func inspect(a Any) {
t := reflect.TypeOf(a)
v := reflect.ValueOf(a)
fmt.Println("Type:", t, "Value:", v)
}
func main() {
var x int = 10
var y float64 = 20.0
inspect(x) // Type: int Value: 10
inspect(y) // Type: float64 Value: 20
}
package main
import (
"fmt"
"reflect"
)
type Human struct {
Name string `json:"name"`
Age int `json:"age"`
}
func main() {
var human Human = Human{
Name: "meowrain",
Age: 20,
}
tp := reflect.TypeOf(human)
vp := reflect.ValueOf(human)
fmt.Println(tp)
fmt.Println(vp)
}
结果:
main.Human
{meowrain 20}
反射的分类
反射在Go中主要有两个方向:
类型反射(Type Reflection): 主要关注于程序运行时获取变量的类型信息。
值反射(Value Reflection): 主要关注于程序运行时获取或设置变量的值。
类型反射
在反射中关于类型还划分为两种:类型(Type)和种类(Kind)。因为在Go语言中我们可以使用type关键字构造很多自定义类型,而种类(Kind)就是指底层的类型,但在反射中,当需要区分指针、结构体等大品种的类型时,就会用到种类(Kind)。 举个例子,我们定义了两个指针类型和两个结构体类型,通过反射查看它们的类型和种类。
Go语言的反射中像数组、切片、Map、指针
等类型的变量,它们的.Name()都是返回空。
https://pkg.go.dev/reflect#Kind
const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Pointer
Slice
String
Struct
UnsafePointer
)
package main
import (
"fmt"
"reflect"
)
type Human struct {
Name string `json:"name"`
Age int `json:"age"`
}
func inspector(data any) {
//看看Kind()的区别
t := reflect.TypeOf(data)
fmt.Println("name:", t.Name())
fmt.Println("kind:", t.Kind())
}
func main() {
fmt.Println("==============传入结构体========================")
var human Human = Human{
Name: "meow",
Age: 20,
}
inspector(human)
fmt.Println("===================================================")
fmt.Println()
fmt.Println("===============传入结构体指针=======================")
inspector(&human)
fmt.Println("===================================================")
fmt.Println()
fmt.Println("=================传入字符串=====================")
var a string = "hello world"
inspector(a)
fmt.Println("======================================")
fmt.Println()
fmt.Println("================传入字符串指针======================")
inspector(&a)
fmt.Println("======================================")
}
> ==============传入结构体========================
name: Human
kind: struct
===================================================
> ===============传入结构体指针=======================
name:
kind: ptr
===================================================
> =================传入字符串=====================
name: string
kind: string
======================================
> ================传入字符串指针======================
name:
kind: ptr
======================================
值反射
reflect.ValueOf()返回的是reflect.Value类型,其中包含了原始值的值信息。reflect.Value与原始值之间可以互相转换。
reflect.Value类型提供的获取原始值的方法如下:
package main
import (
"fmt"
"reflect"
)
type Human struct {
Name string `json:"name"`
Age int `json:"age"`
}
func inspector(data any) {
v := reflect.ValueOf(data)
if v.Kind() == reflect.Struct {
fmt.Println("是结构体")
} else if v.Kind() == reflect.Pointer {
ov := v.Elem()
if ov.Kind() == reflect.Struct {
fmt.Println("是结构体指针")
}
}
if v.Kind() == reflect.String {
fmt.Println("是字符串")
} else if v.Kind() == reflect.Pointer {
ov := v.Elem()
if ov.Kind() == reflect.String {
fmt.Println("是字符串指针")
}
}
}
func main() {
fmt.Println("==============传入结构体========================")
var human Human = Human{
Name: "meow",
Age: 20,
}
inspector(human)
fmt.Println("===================================================")
fmt.Println()
fmt.Println("===============传入结构体指针=======================")
inspector(&human)
fmt.Println("===================================================")
fmt.Println()
fmt.Println("=================传入字符串=====================")
var a string = "hello world"
inspector(a)
fmt.Println("======================================")
fmt.Println()
fmt.Println("================传入字符串指针======================")
inspector(&a)
fmt.Println("======================================")
}
==============传入结构体========================
是结构体
===================================================
===============传入结构体指针=======================
是结构体指针
===================================================
=================传入字符串=====================
是字符串
======================================
================传入字符串指针======================
是字符串指针
======================================
通过反射设置变量的值
想要在函数中通过反射修改变量的值,需要注意函数参数传递的是值拷贝,必须传递变量地址才能修改变量值。而反射中使用专有的Elem()方法来获取指针对应的值。
修改字符串:
package main
import (
"fmt"
"reflect"
)
type Human struct {
Name string `json:"name"`
Age int `json:"age"`
}
func inspector(data any) {
v := reflect.ValueOf(data)
if v.Elem().Kind() == reflect.String {
v.Elem().SetString("meowrain")
}
}
func main() {
str := "helloworld"
inspector(&str)
fmt.Println(str)
}
修改结构体