0x01 golang的反射
- 反射三定律
1.1. 反射第一定律
反射可以将interface类型变量转换成反射对象
var x float64 = 3.4
t := reflect.TypeOf(x) // reflect.Type类型
fmt.Println("type:", t)
v := reflect.ValueOf(x) // reflect.Value类型
fmt.Println("value:", v)
// type: float64
// value: 3.4
注意:反射是针对interface类型变量的,其中 TypeOf() 和 ValueOf() 接受的参数都是 interface{} 类型的,也即x值是被转成了interface传入的。
1.2. 反射第二定律
反射可以将反射对象还原成interface对象
var x float64 = 3.4
v := reflect.ValueOf(x)
var y float64 = v.Interface().(float64)
fmt.Println("value:", y)
// value: 3.4
1.3. 反射第三定律
反射对象可修改,但value必须是可设置的
var x float64 = 3.4
v := reflect.ValueOf(x) // v的类型是reflect.Value 不可寻址
v.SetFloat(7.1) // Error: will panic
// panic: reflect: reflect.Value.SetFloat using unaddressable value
vp := reflect.ValueOf(&x)
vp.Elem().SetFloat(7.1)
fmt.Println("x:", v.Elem().Interface())
// x : 7.1
上例中,传入reflect.ValueOf()函数的其实是x的值,而非x本身。即通过v修改其值是无法影响x的,也即是无效 的修改,所以golang会报错。
想到此处,即可明白,如果构建v时使用x的地址就可实现修改了,但此时v代表的是指针地址,我们设置的是指针所 指向的内容,也即我们想要修改的是 *v 。 那怎么通过v修改x的值呢?
reflect.Value 提供了 Elem() 方法,可以获得指针向指向的 value 。
反射对象是map 和 pointer 时例外,因为他们已经是指针
{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(&a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.Elem().SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}
{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}
{
// struct
a := FooStruct{}
val := reflect.ValueOf(&a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) // {foo2}
}
{
// pointer
a := &FooPointer{}
val := reflect.ValueOf(a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) //&{foo2}
}
- 反射类型reflect.Type和reflect.Value拥有的方法
2.1. Type方法
适用于reflect.Value
此方法返回reflect.Value拥有的类型
x := 6.4
v := reflect.ValueOf(x)
fmt.Println(v.Type())
// float64
2.2. Kind方法
适用于reflect.Type和reflect.Value
此方法返回存储项的常量类型
reflect.Float64
reflect.Uint
...
x := 6.4
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
fmt.Println(reflect.Float64 == v.Kind())
fmt.Println(reflect.Float64 == t.Kind())
2.2.* Kind方法注意事项
Kind方法返回的是reflect.Value的底层类型,并不像Type方法返回的是静态类型
eg..
type MyFloat float64
var flt MyFloat = 6.4
v := reflect.ValueOf(flt)
t := reflect.TypeOf(flt)
fmt.Println(v.Type(), v.Kind()) // main.MyFloat float64
if reflect.Float64 == t.Kind() {
fmt.Println(v.Float()) // 6.4
}
在示例中,reflect.Value的Type方法返回的是静态类型 main.MyFloat ,Kind方法返回的是底层类型float64.