前面讲解了 Value 和 Type 的基本概念,本节重点讲解反射对象 Value、Type 和类型实例之间的相互转化。实例、Value、Type 三者之间的转换关系如下图所示。图:反射对象关系反射 ...
前面讲解了 Value 和 Type 的基本概念,本节重点讲解反射对象 Value、Type 和类型实例之间的相互转化。实例、Value、Type 三者之间的转换关系如下图所示。

图:反射对象关系
反射 API 的分类总结如下。
通过实例获取 Value 对象,直接使用 reflect.ValueOf() 函数。例如:
func ValueOf(i interface {}) Value
通过实例获取反射对象的 Type,直接使用 reflect.TypeOf() 函数。例如:
func TypeOf(i interface{}) Type
Type 里面只有类型信息,所以直接从一个 Type 接口变量里面是无法获得实例的 Value 的,但可以通过该 Type 构建一个新实例的 Value。reflect 包提供了两种方法,示例如下:
//New 返回的是一个 Value,该 Value 的 type 为 PtrTo(typ),即 Value 的 Type 是指定 typ 的指针类型
func New(typ Type) Value
//Zero 返回的是一个 typ 类型的零佳,注意返回的 Value 不能寻址,位不可改变
func Zero(typ Type) Value
如果知道一个类型值的底层存放地址,则还有一个函数是可以依据 type 和该地址值恢复出 Value 的。例如:
func NewAt(typ Type, p unsafe.Pointer) Value
从反射对象 Value 到 Type 可以直接调用 Value 的方法,因为 Value 内部存放着到 Type 类型的指针。例如:
func (v Value) Type() Type
Value 本身就包含类型和值信息,reflect 提供了丰富的方法来实现从 Value 到实例的转换。例如:
//该方法最通用,用来将 Value 转换为空接口,该空接口内部存放具体类型实例
//可以使用接口类型查询去还原为具体的类型
func (v Value) Interface() (i interface{})
//Value 自身也提供丰富的方法,直接将 Value 转换为简单类型实例,如果类型不匹配,则直接引起 panic
func (v Value) Bool () bool
func (v Value) Float() float64
func (v Value) Int() int64
func (v Value) Uint() uint64
从一个指针类型的 Value 获得值类型 Value 有两种方法,示例如下。
//如果 v 类型是接口,则 Elem() 返回接口绑定的实例的 Value,如采 v 类型是指针,则返回指针值的 Value,否则引起 panic
func (v Value) Elem() Value
//如果 v 是指针,则返回指针值的 Value,否则返回 v 自身,该函数不会引起 panic
func Indirect(v Value) Value
指针类型 Type 到值类型 Type。例如:
//t 必须是 Array、Chan、Map、Ptr、Slice,否则会引起 panic
//Elem 返回的是其内部元素的 Type
t.Elem() Type
值类型 Type 到指针类型 Type。例如:
//PtrTo 返回的是指向 t 的指针型 Type
func PtrTo(t Type) Type
Value 值的修改涉及如下两个方法:
//通过 CanSet 判断是否能修改
func (v Value ) CanSet() bool
//通过 Set 进行修改
func (v Value ) Set(x Value)
Value 值在什么情况下可以修改?我们知道实例对象传递给接口的是一个完全的值拷贝,如果调用反射的方法 reflect.ValueOf() 传进去的是一个值类型变量, 则获得的 Value 实际上是原对象的一个副本,这个 Value 是无论如何也不能被修改的。
如果传进去的是一个指针,虽然接口内部转换的也是指针的副本,但通过指针还是可以访问到最原始的对象,所以此种情况获得的 Value 是可以修改的。下面来看一个简单的示例。
package main
import (
"fmt"
"reflect"
)
type User struct {
id int
Name string
Age int
}
func main() {
u := User{Id: 1, Name:"andes", Age: 20}
va := reflect.ValueOf(u)
vb := reflect.ValueOf(&u)
//值类型是可修改的
fmt.Println(va.CanSet(), va.FieldByName("Name").CanSet()) //false false
//指针类型是可修改的
fmt.Println(vb.CanSet(), vb.Elem().FieldByName("Name").CanSet()) //false false
fmt.Printf("%v\n", vb)
name :="shine"
vc := reflect.ValueOf(name)
//通过 Set 函数修改变量的值
vb.Elem().FieldByName("Name").Set(vc)
fmt.Printf("%v\n", vb)
}运行结果:
false false
false true
&{1 andes 20)
&{1 shine 20)
这里归纳出了反射的三条定律:
反射可以从接口值得到反射对象。
反射可以从反射对象获得接口值。
若要修改一个反射对象,则其值必须可以修改。