- 定律一:反射可以将“接口类型变量”转换为“反射类型对象”
func TypeOf(i interface{}) Type
func ValueOf(i interface{}) Value
执行函数 reflect.TypeOf ()和 reflect.ValueOf()的入参都是作为一个接口传入,返回反射类型的对象。
- 定律二:反射可以将“反射类型对象”转换为“接口类型变量”
func (v Value) Interface() (i interface{})
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 30
v := reflect.ValueOf(a) //返回Value类型对象
fmt.Println(v)
v1 := v.Interface() //返回空接口变量
v2 := v1.(int) //类型断言,断定v1中type=int
fmt.Printf("%T %v\n", v2, v2)
fmt.Println(v2)
}
- 定律三:如果要修改“反射类型对象”,其值必须是“可写的”(settable)
案例一:
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("v:", v.CanSet())//判断可写性 false
v.SetFloat(7.1) //panic.
x到v是一个值拷贝,如果v.SetFloat(7.1)
这行代码能够成功执行,它不会更新 x ,虽然看起来变量 v 是根据 x 创建的。相反,它会更新 x 存在于 反射对象 v 内部的一个拷贝,而变量 x 本身完全不受影响。这会造成迷惑,并且没有任何意义,所以是不合法的。“可写性”就是为了避免这个问题而设计的。
案例二:
既然是值拷贝,那我们传递地址会怎么样?
var x float64 = 3.4
p := reflect.ValueOf(&x) // &x.
fmt.Println("type of p:", p.Type())//*float64
fmt.Println("settability of p:", p.CanSet())//false
v.SetFloat(7.1) //panic.
同样不能做修改,这是因为v当前保存的是x的地址,而不是x的原始空间。
fmt.Println(p) //0xc420014060
因此我们可以可以调用 Value 类型的 Elem 方法。Elem 方法能够对指针进行“解引用”。
package main
import (
"fmt"
"reflect"
)
func main(){
var x float64 = 3.4
p := reflect.ValueOf(&x)
fmt.Println(p)//0xc420014060
fmt.Println("p:", p.CanSet())//false
v:=p.Elem()//Elem()对指针解引用
fmt.Println("v:", v.CanSet())//true
v.SetFloat(6.6)
fmt.Println(v)//6.6
}