Go 语言提供了一种机制,能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型。这种机制被称为 反射 。
但反射是把双刃剑,功能强大但代码可读性并不理想,若非必要并不推荐使用反射。
在 Go 中, reflect
包实现了运行时反射。reflect
包会帮助识别 interface{}
变量的底层具体类型和具体值。
reflect.Type
reflect.Type
表示 interface{}
的具体类型。reflect.TypeOf()
方法返回 reflect.Type
。
像我们之前讲过的空接口参数的函数,可以通过类型断言来判断传入变量的类型,也可以借助反射来确定传入变量的类型。
package main
import (
"fmt"
"reflect"
)
func reflectType(x interface{}) {
obj := reflect.TypeOf(x)
fmt.Println(obj)
}
func main() {
var a int64 = 123
reflectType(a)
var b float64 = 3.14
reflectType(b)
}
该程序运行后输出结果如下:
reflect.Value
reflect.Value
表示 interface{}
的具体值。reflect.ValueOf()
方法返回 reflect.Value
。
package main
import (
"fmt"
"reflect"
)
func reflectType(x interface{}) {
typeX := reflect.TypeOf(x)
valueX := reflect.ValueOf(x)
fmt.Println(typeX)
fmt.Println(valueX)
}
func main() {
var a int64 = 123
reflectType(a)
var b float64 = 3.14
reflectType(b)
}
该程序运行后输出结果如下:
relfect.Kind
relfect.Kind
表示的是种类。在使用反射时,需要理解类型(Type)和种类(Kind)的区别。编程中,使用最多的是类型,但在反射中,当需要区分一个大品种的类型时,就会用到种类(Kind)。
Go 语言程序中的类型(Type)指的是系统原生数据类型,如 int
、 string
、 bool
、 float32
等类型,以及使用 type
关键字定义的类型,这些类型的名称就是其类型本身的名称。例如使用 type A struct{}
定义结构体时,A
就是 struct{}
的类型。
种类(Kind)指的是对象归属的品种,在 reflect
包中有如下定义:
// A Kind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type Kind uint
const (
Invalid Kind = iota
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
)
通过下面这个程序,相信你会很容易明白这两者的区别:
package main
import (
"fmt"
"reflect"
)
func reflectType(x interface{}) {
typeX := reflect.TypeOf(x)
fmt.Println(typeX.Kind()) // struct
fmt.Println(typeX) // main.cat
}
type cat struct {
}
func main() {
var a cat
reflectType(a)
}
该程序运行后输出结果如下:
relfect.NumField()
relfect.NumField()
方法返回结构体中字段的数量。
package main
import (
"fmt"
"reflect"
)
func reflectNumField(x interface{}) {
// 检查 x 的类别是 struct
if reflect.ValueOf(x).Kind() == reflect.Struct {
v := reflect.ValueOf(x)
fmt.Println("Number of fields", v.NumField())
}
}
type cat struct {
name string
age int
}
func main() {
var a cat
reflectNumField(a)
}
该程序运行后输出结果如下:
relfect.Field()
relfect.Field(i int)
方法返回字段 i
的 reflect.Value
。
package main
import (
"fmt"
"reflect"
)
func reflectNumField(x interface{}) {
// 检查 x 的类别是 struct
if reflect.ValueOf(x).Kind() == reflect.Struct {
v := reflect.ValueOf(x)
fmt.Println("Number of fields", v.NumField())
for i := 0; i < v.NumField(); i++ {
fmt.Printf("Field:%d type:%T value:%v\n", i, v.Field(i), v.Field(i))
}
}
}
type cat struct {
name string
age int
}
func main() {
var a = cat{"哆啦A梦", 10}
reflectNumField(a)
}
该程序运行后输出结果如下:
Number of fields 2
Field:0 type:reflect.Value value:哆啦A梦
Field:1 type:reflect.Value value:10
参考文献:
[1] Alan A. A. Donovan; Brian W. Kernighan, Go 程序设计语言, Translated by 李道兵, 高博, 庞向才, 金鑫鑫 and 林齐斌, 机械工业出版社, 2017.