1. 反射定律
- 反射可以将“接口类型变量”转换为“反射类型对象”。
- 反射可以将“反射类型对象”转换为“接口类型变量”。
- 如果要修改“反射类型对象”,其值必须是“可写的”。
a. “接口类型变量”=>“反射类型对象”
所谓的反射类型,就是reflect.Type和reflect.Value
var a int = 30
v := reflect.ValueOf(a) //返回Value类型对象,值为30
t := reflect.TypeOf(a) //返回Type类型对象,值为int
fmt.Println(v)
fmt.Println(t)
v = reflect.ValueOf(&a) //返回Value类型对象,值为&a,变量a的地址
t = reflect.TypeOf(&a) //返回Type类型对象,值为*int
fmt.Println(v)
fmt.Println(t)
上面的案例通过使用reflect.ValueOf和reflect.TypeOf将接口类型变量分别转换为反射类型对象value和type。
- value中包含了接口中的实际值。
- type中包含了接口中的实际类型。
事实上,reflect.ValueOf和reflect.TypeOf的参数类型都是interface{},空接口类型,而返回值的类型是reflect.Value和reflect.Type,中间的转换由reflect包来实现。
b. “反射类型对象”=>“接口类型变量”
基本方法是: reflectValue值.Interface.(要转化为的类型)
根据一个reflect.Value类型(注意没有reflect.Type)的变量,我们可以使用 Interface方法恢复其接口类型的值。事实上,这个方法会把 type 和 value信息打包并填充到一个接口变量中,然后返回。
var a int = 30
value := reflect.ValueOf(&a) //返回Value类型对象,值为&a,变量a的地址
t := value.Interface().(*int) //类型断言,断定v1中type=*int
fmt.Printf("%T %v\n", t, t) // *int 0xc420086008
fmt.Println(*t) // 30
cir := 6.28
value2 := reflect.ValueOf(cir)
t2 := value2.Interface().(float64)
fmt.Printf("%T %v\n", t2, t2) // float64 6.28
fmt.Println(t2) // 6.28
t3 := value2.Interface().(int) //error : interface conversion: interface {} is float64, not int
fmt.Println(t3)
最关键的两步
v1 := value.Interface() // 返回的是一个接口变量
v2 := v1.(float64) // 再判断这个接口变量是否能转化为某类型变量
c. 修改“反射类型对象”
基本方法: 指针类型的value.Elem().SetFloat(待赋的新值)
注意这里用到的也是`reflect.ValueOf`
var circle float64 = 6.28
value := reflect.ValueOf(circle)
fmt.Println(value.CanSet()) // false
fmt.Println(value) // 6.28
value2 := reflect.ValueOf(&circle)
fmt.Println(value2.CanSet()) // false
fmt.Println(value2) // 0xc420086008
value3 := reflect.ValueOf(&circle).Elem()
fmt.Println(value3.CanSet()) // true
fmt.Println(value3) // 6.28
value3.SetFloat(3.14)
fmt.Println(circle) // 3.14
2. reflect.Type
reflect.TypeOf(i interface{}) Type
因为reflect.Typeof的参数是空接口类型,因此可以接收任意类型的数据。 TypeOf()的返回值是这个接口类型对应的reflect.Type对象。
reflect.Type的定义方法
比如,对于一个结构体
type User struct {
Name string
Age int
Sex string
}
func (user User) Say (msg string) error{
fmt.Println("say",msg)
return nil
}
func reflectionStruct(){
user :=User{"wang",1,"m"}
rType := reflect.TypeOf(user)
fmt.Println(rType)
fmt.Printf("%T,%T,%v,%v\n",user,rType,user,rType)
fmt.Println("---------------------")
// 数据类名
fmt.Println(rType) // main.User
// 数据类型
fmt.Println(rType.Kind()) // struct
//数据的类名
fmt.Println(rType.Name()) // User
//数据的完整全名(包含包)
fmt.Println(rType.String()) // main.User
//数据对象的字段个数
fmt.Println(rType.NumField()) // 3
//数据对象的方法个数
fmt.Println(rType.NumMethod()) // 1
fmt.Println("---------------------")
rValue := reflect.ValueOf(user)
fmt.Println(rValue) // {wang 1 m}
fmt.Println(rValue.Kind()) // struct
fmt.Println(rValue.String()) // <main.User Value>
fmt.Println(rValue.NumMethod()) // 1
fmt.Println(rValue.NumField()) // 3
3. reflect.Value
reflect.ValueOf(i interface{}) Value
- reflect.ValueOf()的返回值类型为reflect.Value,它实现了interface{}参数到reflect.Value的反射
- reflact.Value对象可以通过调用Interface()方法,再反射回interface{}对象
- 修改变量数据,通过变量指针类型的value.Elem(),可调用Set...
- 结构体属性的修改和方法的调用
a. 对于一般类型
见上面反射定律的例子
b. 对于结构体类型
可以通过Field和Method方法修改属性和调用方法
Field
- 修改变量一定需要通过Elem()
- 可使用FieldByName 或者Field(index)
type User struct {
Name string
Age int
Sex string
}
func main(){
user := User{"小小", 23,"Man"}
rvalue := reflect.ValueOf(&user)
rvalue.Elem().FieldByName("Sex").SetString("Woman")
fmt.Println(user) // {小小 23 Woman}
rvalue.Elem().Field(1).SetInt(10)
fmt.Println(user) // {小小 10 Woman}
}
Method
- 函数的调用用`MethodByName()`或者`Method(index)`的call方法
- 返回值是一个数组
type User struct {
Name string
Age int
Sex string
}
func (user User) Say (msg string) int {
fmt.Println("say",msg)
return 1234
}
func main(){
args := []reflect.Value{reflect.ValueOf("hello 1")}
method := rvalue.Method(0)
method.Call(args) // say hello 1
method1 := rvalue.MethodByName("Say")
t:= method1.Call(args) // say hello 1
fmt.Println(t) // [<int Value>]
fmt.Println(t[0]) // 1234
}