由于没有强类型语言的经验,反射这个概念,以前确实没怎么接触过。在维基百科上搜了一下,具体解释以下:编程
在计算机学中,反射式编程(英语:reflective programming)或反射(英语:reflection),是指计算机程序在运行时(runtime)能够访问、检测和修改它自己状态或行为的一种能力。用比喻来讲,反射就是程序在运行的时候可以“观察”而且修改本身的行为。markdown
go 中的反射也是这种做用,能够在程序运行期间,获取变量的类型与值的信息,而后进行访问或或者修改。go 语言中,内置了 reflect
包,用来获取一个变量的类型(type
)与值(value
),对应的方法分别为 reflect.TypeOf()
和 reflect.ValueOf()
。函数
TypeOf
方法,会返回该变量的类型对象,类型对象下能够获取到变量的类型与种类。spa
import (
"fmt"
"reflect"
)
func main() {
// 定义一个int类型的变量
var i int = 1
// 获取变量的类型对象
var typeOfNum = reflect.TypeOf(i)
// 输出类型与种类
typeOfNumName = typeOfNum.Name()
typeOfNumKind = typeOfNum.Kind()
fmt.Printf("name: %s, kind: %s", typeOfNumName, typeOfNumKind)
}
复制代码
能够看到,此时的类型与种类都为 int
。指针
类型表示定义变量的时候指定的类型,能够反映 type
关键字定义的类型,而种类是变量最终归属的类型。提及来可能比较苍白,咱们直接上代码。code
type num int
// 定义一个num类型的变量
var i num = 1
var typeOfNum = reflect.TypeOf(i)
复制代码
能够看到,此时的类型为 num
,种类为 int
。orm
对于一些引用类型的变量,好比切片、函数、结构体,kind
都能准确反映其底层的类型。对象
func printTypeOf(typeOf reflect.Type) {
fmt.Printf("name: %s, kind: %s\n", typeOf.Name(), typeOf.Kind())
}
type Person struct {}
type IntSlice []int
func main() {
var a = IntSlice{}
var b = Person{}
printTypeOf(reflect.TypeOf(a))
printTypeOf(reflect.TypeOf(b))
}
复制代码
而面对匿名结构体或者匿名函数,其类型值会返回为空。索引
func main() {
var a = struct {}{}
printTypeOf(reflect.TypeOf(a))
}
复制代码
ValueOf
方法,能够获取一个变量的值。string
var i = 3.1415926
var s = "欢迎关注个人公众号:『天然醒的笔记本』"
fmt.Println(reflect.ValueOf(s))
fmt.Println(reflect.ValueOf(i))
复制代码
经过反射的值对象,也能取到变量的种类,而且还能根据其种类,调用对应的方法获取变量的真实值。
var i = 100
var v = reflect.ValueOf(i)
fmt.Println(v.Int()) // 若是值是 Int 类型,能够经过 Int 方法获取具体值
fmt.Println(v.Kind())
复制代码
经过反射获得的值对象,能够对变量自己的值进行修改。首先,在获取反射值时,不能直接获取变量的反射值,而是要先取其指针的值对象。
var i = 100
var v = reflect.ValueOf(&i) // 取出变量i的指针的值对象
fmt.Println(v.Kind(), v)
复制代码
取出指针的值对象以后,不能当即赋值,由于此时拿到的是变量的地址。
要赋值的话,须要先调用 Elem
方法,取出具体元素,而后进行赋值。
var i = 100
var v = reflect.ValueOf(&i) // 取出变量i的指针的值对象
var e = v.Elem()
e.SetInt(500) // 修改元素值
fmt.Println(e.Kind(), i)
复制代码
前面介绍过,经过反射能够获得变量的值,对于结构体来讲,也是同样。
type Person struct {
name string
age int
gender string
address string
}
var p = Person{"Shenfq", 25, "男", "湖南长沙"}
var v = reflect.ValueOf(p)
fmt.Println(v.Kind(), v)
复制代码
反射值对象还提供了一些方法,专门用来针对结构体成员的信息获取。
NumField()
能够获取结构体成员的具体数量。
var p = Person{"Shenfq", 25, "男", "湖南长沙"}
var v = reflect.ValueOf(p)
fmt.Println("Person 结构体成员数:", v.NumField())
复制代码
Field()
能够获取结构体指定索引位置的成员的反射值。
var p = Person{"Shenfq", 25, "男", "湖南长沙"}
var v = reflect.ValueOf(p)
var num = v.NumField()
for i :=0; i < num; i++ {
var val = v.Field(i)
fmt.Printf("Person[%d]: %s %v\n", i, val.Type(), val)
}
复制代码
FieldByName()
能够获取结构体指定成员名称的成员的反射值。
var p = Person{"Shenfq", 25, "男", "湖南长沙"}
var v = reflect.ValueOf(p)
var vOfName = v.FieldByName("name")
fmt.Printf("Person[name]: %s %v\n", vOfName.Type(), vOfName)
复制代码