Package reflect实现了运行时反射,允许程序获取任意对象的运行时信息。最常见的用法就是通过调用TypeOf方法获取对象类型;调用ValueOf返回对象运行时数据。反射是程序检查自身结构的一种能力,是一种形式的元编程。
类型和接口(Types and interfaces)
反射构建在类型系统之上。Go是静态类型语言,每个变量都有一个静态类型,在编译阶段确认。
1 | type MyInt int |
变量i的类型为int,j的类型为MyInt,虽然i和j的底层类型都是int类型,但它们之间如果不进行类型转换则不能进行赋值。interface{}可以表示任何类型的数据。
Go反射三大法则
1.从interface{}开始进行对象的反射
简单来说反射就是一种从`interface`类型的变量取出里面的`Type`和`value`的一种机制。通过`reflect.TypeOf`和`reflect.ValueOf` 可以分别获取反射的类型和值。
1 | func main() { |
上述代码表明变量x的类型为float64,而且传递给函数TypeOf的参数x是float64类型,而不是interface类型,所以这跟interface又有什么关系呢?
查看函数TypeOf源码1
2
3
4
5
6// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
发现TypeOf的参数类型为interface{},所以参数x会存储在一个空的interface中,然后再从这个interface中恢复类型信息。func ValueOf(i interface{}) Value也是如此。
2.从反射对象可以获得interface的值
对于一个reflect.Value类型的对象(反射对象),我们可以通过Interfaace方法从中获取interface值。1
2
3
4
5
6func main() {
var x float64 = 3.4
r := reflect.ValueOf(x)
fmt.Println(r.Interface())
// output: 3.4
}1
2
3func (v Value) Interface() (i interface{}) {
return valueInterface(v, true)
}reflect.ValueOf的作用是把float64类型的变量转换成reflect.Value类型,而Interface的作用与reflect.ValueOf正好相反。
3.要想修改一个反射对象,反射对象必须是可设置的
执行如下代码1
2
3
4
5func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1)
}
所以报错的原因是什么呢?请看如下代码:1
2var x float64 = 3.4
v := reflect.ValueOf(x)
因为Go语言的函数调用的参数都是值传递的,所以传递给reflect.ValueOf函数的参数x并不是x本身而是x的一份拷贝。如果v.SetFloat(7.1)可以设置成功,这并不能更新x的值,更新的只是x的拷贝;但是真正的x并没有更新,这样做不但没有用而且会造成很多的疑惑。
要想对原有变量进行修改可以采用如下方式:
1
2
3
4
5
6
7
8
func main() {
i := 3.4
v := reflect.ValueOf(&i) //获取变量指针
v.Elem().SetFloat(10) //通过Elem()获取指针指向的变量,SetFloat对变量进行更新
fmt.Println(i)
//output :10
}
1 | func main() { |
通过反射对结构体进行修改:1
2
3
4
5
6
7
8func main() {
t := T{23, "skidoo"}
s := reflect.ValueOf(&t).Elem()
s.Field(0).SetInt(10)
fmt.Println(s.Field(0))
// output: 10
}
Ref:
1.https://golang.org/pkg/reflect/
2.https://blog.golang.org/laws-of-reflection