反射
代码在运行过程中,动态获取并修改代码自身的能力,就是反射。
为啥用反射
对于静态类型语言,为了提高灵活性,有时在代码里允许传入多种类型变量,编译的时候不清楚会用哪一种,只有代码执行的时候才能动态感知到。通过反射只能获取 Golang 内置类型,用类型断言则可以获取所有类型(包括用户自定义类型)。
示例
通过反射查看变量类型和值
package main
import (
"fmt"
"reflect"
)
type People struct {
Name string
Age int
No int
}
type Student struct {
SNo int
People
}
func main() {
i := 333
f(i)
s := Student{1, People{"kika", 33, 1}}
f(s)
p := People{"lisi", 22, 123}
f(p)
f(&p)
}
func f(i interface{}) {
t := reflect.TypeOf(i) // 真实类型,包括用户自定义类型
v := reflect.ValueOf(i)
k := v.Kind() // golang 内置类别,例如 map,struct,string,bool,int 等
if k == reflect.Ptr {
e := v.Elem() // 如果传入的是指针,通过 Elem() 拿到指向的值
fmt.Println("Elem is:", e)
}
fmt.Printf("type is: %s\nvalue is: %v\nkind is: %s\n\n", t, v, k)
fmt.Println()
}
输出:
type is: int
value is: 333
kind is: int
type is: main.Student
value is: {1 {kika 33 1}}
kind is: struct
type is: main.People
value is: {lisi 22 123}
kind is: struct
Elem is: {lisi 22 123}
type is: *main.People
value is: &{lisi 22 123}
kind is: ptr
通过反射把变量转为具体类型的值
通过 reflect.ValueOf(i)
得到的 reflect.Value
类型不是基本类型,需要再调用 reflect 包的具体类型转换函数得到具体类型,从而实现跟类型断言一样的效果。只支持 golang 的内置类型转换。
例如:
func (v Value) Int() int64
func (v Value) OverflowInt(x int64) bool
func (v Value) Uint() uint64
func (v Value) OverflowUint(x uint64) bool
func (v Value) Float() float64
func (v Value) OverflowFloat(x float64) bool
func (v Value) Complex() complex128
func (v Value) OverflowComplex(x complex128) bool
func (v Value) Bytes() []byte
func (v Value) String() string
示例:
package main
import (
"fmt"
"reflect"
)
func main() {
a := 3
f(a)
}
func f(i interface{}) {
rVal := reflect.ValueOf(i)
fmt.Printf("%T, %v\n", rVal, rVal)
// fmt.Println(rVal + 3) // invalid operation: rVal + 3 (mismatched types reflect.Value and int)
rInt := rVal.Int()
fmt.Println(rInt + 3)
}
通过反射修改变量值(需要传地址)
package main
import (
"fmt"
"reflect"
)
func main() {
s := "hello world"
fmt.Println(s)
reflect.ValueOf(&s).Elem().SetString("hello golang")
fmt.Println(s)
}
通过反射操作结构体
可以查看结构体元素、方法,并调用方法:
package main
import (
"fmt"
"reflect"
)
type People struct {
Name string `json:"name"`
Age int
No int
}
func (p People) Eat() {
fmt.Println("People eating")
}
func main() {
p := People{"kika", 29, 33332222}
fmt.Println(p)
rVal := reflect.ValueOf(p)
rType := reflect.TypeOf(p)
nf := rVal.NumField()
fmt.Println("NumField is: ", nf) // 字段个数
for i := 0; i < nf; i++ {
iv := rVal.Field(i)
fmt.Print(iv)
tag := rType.Field(i).Tag.Get("json")
if tag != "" {
fmt.Print(", tag is:", tag)
}
fmt.Println()
}
nm := rVal.NumMethod()
fmt.Println("NumMethod is: ", nm) // 方法个数
for i := 0; i < nm; i++ {
iv := rVal.Method(i)
fmt.Printf("Method is: %v\n", iv)
ret := iv.Call([]reflect.Value{}) // 调用方法
fmt.Println(ret)
}
}
package main
import (
"fmt"
"reflect"
)
type M struct{}
type In struct{}
type Out struct{}
func (m *M) Example(in In) Out {
fmt.Println("call Example in M")
return Out{}
}
func main() {
v := reflect.ValueOf(&M{})
m := v.MethodByName("Example")
in := m.Type().In(0)
out := m.Type().Out(0)
fmt.Println(in, out)
inVal := reflect.New(in).Elem()
rtn := m.Call([]reflect.Value{inVal})
fmt.Println(rtn[0])
}