前置条件:学习过 Go 中的 interface 和 interface{} 特性,Reflect 特性的实现是基于 interface 的底层表示来实现的
变量的内置 pair
在理解 Reflect 特性前, 我们需要对 Go 中变量的底层结构有一个基本的理解
Go 中所有的变量在底层都存在一个键值对 pair
- type(二者选一)
- static type(int、string… 等基本类型)
- concrete type(interface 所指向的具体类型)
- value
反射,即通过该 pair 来得到变量的 type 和 值
1 2 3 4 5 6 7 8 9 10 11 12 13
| package reflecttest1
import "fmt"
func Reflecttest1() { a := "aceld"
var allType interface{} = a
str, _ := allType.(string) fmt.Println(str)
}
|
由上述代码可见,不管接口指针如何改变,变量内容中的 pair 始终不变
反射机制用法
不多说,上代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package reflecttest2
import ( "fmt" "reflect" )
func reflectNum(arg interface{}) { fmt.Println("type = ", reflect.TypeOf(arg)) fmt.Println("value = ", reflect.ValueOf(arg)) }
func ReflectTest2() { num := 1.2345
reflectNum(num) }
|
以上我们通过反射获取了变量的基本类型,接下来我们试着获取变量的复杂类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| type User struct { Name string Id int Age int }
func (u User) Call() { fmt.Println("User is called...") fmt.Printf("%v\n", u) }
func DoFiledAndMethod(input interface{}) { fmt.Println("DoFiledAndMethod is called...") inputType := reflect.TypeOf(input)
inputValue := reflect.ValueOf(input)
fmt.Println("inputType is ", inputType.Name()) fmt.Println("inputValue is ", inputValue) for i := 0; i < inputType.NumField(); i++ { field := inputType.Field(i) value := inputValue.Field(i).Interface() fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
} for i := 0; i < inputType.NumMethod(); i++ { method := inputType.Method(i) fmt.Printf("%s: %v\n", method.Name, method.Type) } }
func ReflectTest2() { user := User{"Aceld", 1, 18}
DoFiledAndMethod(user) }
|
通过 Reflect 机制,我们可以对实现 Go 中的结构体标签特性
反射与接口的区别
大致来讲,interface 用于在编译时实现多态,实行静态类型检查,实现通用的类型行为
而 reflect 用于在程序运行时检测变量的类型与值,用来实现动态的类型操作与多态
注意这里静态与动态的概念与 C++ 中不一致

Relect 实战案例
我认为很好的 Golang 学习教程_