前面的文章主要介绍了 Go 包依赖管理 GOPATH 和 Go Module 的应用实践。本文将会介绍 Go 反射相关的内容。
反射是一项功能强大的工具,它给开发人员提供了在运行时对代码本身进行访问和修改的能力。接下来我们介绍 Go 反射中 Type 和 Value 两个重要的概念。
我们首先定义一些简单的结构体和方法,用于我们后面的实验验证,代码如下所示,主要位于 feature/relection/reflection.go 文件下:
package main
import "fmt"
// 定义一个人的接口
type Person interface {
// 和人说hello
SayHello(name string)
// 跑步
Run() string
}
type Hero struct {
Name string
Age int
Speed int
}
func (hero *Hero) SayHello(name string) {
fmt.Println("Hello " + name, ", I am " + hero.Name)
}
func (hero *Hero) Run() string{
fmt.Println("I am running at speed " + string(hero.Speed))
return "Running"
}
上述代码中我们定义了一个 Person 接口,以及定义了 Hero 结构体来实现 Person 接口中的方法,同时 Hero 结构体中还包含 3 个成员字段。
Go 的反射与 Java 等语言有不小的区别,主要通过 Type 和 Value 两个基本概念来阐述。其中 Type 主要用于表示被反射变量的类型信息,而 Value 用于表示被反射变量自身的实例信息。Go 的反射实现主要位于 reflect 包中。
通过 reflect#TypeOf 方法,我们可以轻松地获取一个变量的类型信息 reflect.Type。通过 reflect.Type 类型对象,我们访问到其对应类型的各项类型信息。我们可以创建一个 Hero 结构体,通过 reflect#TypeOf 来查看其对应的类型信息,代码如下所示:
func main() {
// 获取实例的反射类型对象
typeOfHero := reflect.TypeOf(Hero{})
fmt.Printf("Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())
}
运行结果如下所示:
Hero's type is main.Hero, kind is struct
在 Go 中,存在着 Type (类型)和 Kind (种类) 区别,如上面结果所展示,Hero 的类型是 main.Hero,种类是 struct。Type 是指变量所属的类型,包括系统的原生数据类型如 int、string等和我们通过 type 关键字定义的类型,比如我们定义的 Hero 结构体,这些类型的名称一般就是其类型本身。而 Kind 是指变量类型的所归属的品种,参考 reflect.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
)
一般我们通过 type 关键字定义的结构体都属于 Struct,而指针变量的种类统一为 Ptr,比如下面代码:
fmt.Printf("*Hero's type is %s, kind is %s",reflect.TypeOf(&Hero{}), reflect.TypeOf(&Hero{}).Kind())
上述代码中通过 reflect#TypeOf 获取了 Hero 指针的类型对象,它的输出将会是:
*Hero's type is *main.Hero, kind is ptr
这说明 &Hero{} 的类型是 *main.Helo,归属于种类 ptr。对于指针类型的变量,可以使用 Type#Elem 获取到指针指向变量的真实类型对象,如下例子所示:
typeOfPtrHero := reflect.TypeOf(&Hero{})
fmt.Printf("*Hero's type is %s, kind is %s\n",typeOfPtrHero, typeOfPtrHero.Kind())
typeOfHero = typeOfPtrHero.Elem()
fmt.Printf(" typeOfPtrHero elem to typeOfHero, Hero's type is %s, kind is %s", typeOfHero, typeOfHero.Kind())
预期输出为:
*Hero's type is *main.Hero, kind is ptr
typeOfPtrHero elem to typeOfHero, Hero's type is main.Hero, kind is struct
通过 typeOfPtrHero#Elem,我们可以获取到 *main.Helo 指针原类型 main.Hero 的类型对象。
本文主要介绍了 Go 语言的反射基础。通过反射,我们可以拿到丰富的类型信息,比如变量的字段名称、类型信息和结构体信息等,并通过这些类型信息做一些灵活的工作。Go 的反射实现了反射的大多数功能,获取类型信息需要配合使用标准库中的词法、语法解析器和抽象语法树对源码进行扫描。
下一篇文章将会继续介绍 Go 语言的反射 reflect 相关内容。