这里先抛出GO语言的反射三定律,然后一一进行讲解:
1、反射可以将“接口类型变量”转换为“反射类型对象”
2、反射可以将“反射类型对象”转换为“接口类型变量”
3、如果要修改反射类型对象,其值必须是“addressable”
谈到Go的反射,涉及到如下几个概念。
(1)数据类型。go语言中的数据类型有:
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
(2)接口类型
接口类型,一种特殊的数据类型,包括一组方法集,同时,其内部维护两个属性:value和type。
value指的是实现了接口类型的类型值;type则是对相应类型值的类型描述。
举例说明:
var i interface{} //定义空接口变量i,值为nil
var a integer = 10 //定义integer类型变量a
i = a //因为任何类型都实现了空接口类型interface{},所以这里的赋值没问题
此时,接口变量i中就包含属性(10,integer)
(3)反射
go中的反射是通过reflect包实现的。通过反射机制,可以获取接口变量存储的类型以及相应的值。
reflect包定义了两种反射类型:Type和Value
Type is the representation of a Go type.
Value is the reflection interface to a Go value.
反射定律一:反射可以将“接口类型变量”转换为“反射类型对象”
注意:这里的反射类型指的是reflect.Type和reflect.Value
将接口类型变量转换为反射类型变量,是通过reflect包的TypeOf和ValueOf实现的。
func TypeOf
func TypeOf(i interface{}) Type
TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.
func ValueOf
func ValueOf(i interface{}) Value
ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.
举例:
package main
import (
"fmt"
"reflect"
)
func main() {
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)
}
运行结果:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
30
int
0xc04203a1d0
*int
成功: 进程退出代码 0.
上面的案例通过使用reflect.ValueOf和reflect.TypeOf将接口类型变量分别转换为反射类型对象v和t。
v中包含了接口中的实际值。
t中包含了接口中的实际类型。
大家可能对上面的案例感到疑惑,程序里没有接口类型变量啊,哪来的接口类型变量到反射类型对象的转换啊?
事实上,reflect.ValueOf和reflect.TypeOf的参数类型都是interface{},空接口类型,而返回值的类型是reflect.Value和reflect.Type,中间的转换由reflect包来实现。
反射定律二:反射可以将“反射类型对象”转换为“接口类型变量”
根据一个 reflect.Value 类型的变量,我们可以使用 Interface 方法恢复其接口类型的值。事实上,这个方法会把 type 和 value 信息打包并填充到一个接口变量中,然后返回。
func (Value) Interface
func (v Value) Interface() (i interface{})
Interface returns v's current value as an interface{}. It is equivalent to:
var i interface{} = (v's underlying value)
It panics if the Value was obtained by accessing unexported struct fields.
举例:
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 30
v := reflect.ValueOf(&a) //返回Value类型对象,值为&a,变量a的地址
t := reflect.TypeOf(&a) //返回Type类型对象,值为*int
fmt.Println(v)
fmt.Println(t)
v1 := v.Interface() //返回空接口变量
v2 := v1.(*int) //类型断言,断定v1中type=*int
fmt.Printf("%T %v\n", v2, v2)
fmt.Println(*v2)
}
运行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
0xc042046130
*int
*int 0xc042046130
30
成功: 进程退出代码 0.
反射定律三:如果要修改反射类型对象,其值必须是“addressable”
通过反射定义一可以知道,反射对象包含了接口变量中存储的值以及类型。
如果反射对象中包含的值是原始值,那么可以通过反射对象修改原始值;
如果反射对象中包含的值不是原始值(反射对象包含的是副本值或指向原始值的地址),那么该反射对象是不可以修改的。
通过CanSet函数可以判定反射对象是否可以修改。
func (Value) CanSet
func (v Value) CanSet() bool
CanSet reports whether the value of v can be changed. A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields. If CanSet returns false, calling Set or any type-specific setter (e.g., SetBool, SetInt) will panic.
举例:
package main
import (
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
}
运行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
panic: reflect: reflect.Value.SetFloat using unaddressable value
goroutine 1 [running]:
panic(0x470400, 0xc042006150)
c:/go/src/runtime/panic.go:500 +0x1af
reflect.flag.mustBeAssignable(0x8e)
c:/go/src/reflect/value.go:228 +0x109
reflect.Value.SetFloat(0x46fa40, 0xc042006148, 0x8e, 0x401c666666666666)
c:/go/src/reflect/value.go:1388 +0x36
main.main()
E:/project/go/proj/src/test/test.go:10 +0xa1
exit status 2
错误: 进程退出代码 1.
举例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
}
运行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: false
成功: 进程退出代码 0.
上面的反射对象v不可以修改,是因为v中保存的是3.4的副本。
举例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println("settability of v:", v.CanSet())
}
运行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: false
成功: 进程退出代码 0.
上面的反射对象v不可以修改,是因为v当前保存的是x的地址,而不是x的原始空间。
举例:
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
v := reflect.ValueOf(&x).Elem()
fmt.Println("settability of v:", v.CanSet())
v.SetFloat(6.6)
fmt.Println("x=", x)
}
运行:
C:/go/bin/go.exe run test.go [E:/project/go/proj/src/test]
settability of v: true
x= 6.6
成功: 进程退出代码 0.
从运行结果,可以看出,这时候的反射对象v是可以修改的,v的修改等同于x的修改。
上面的代码里,出现了Elem函数,Elem用来获取原始值对应的反射对象。
func (Value) Elem
func (v Value) Elem() Value
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil.
关于go的反射机制,就说这么多吧。
原文地址:http://www.jb51.net/article/90021.htm