最近在看一些go語言标準庫以及第三方庫的源碼時,發現go的reflect被大量使用,雖然反射的機制大多數語言都支援,但好像都沒有go一樣這麼依賴反射的特性。個人覺得,reflect使用如此頻繁的一個重要原因離不開go的另一個特性,空接口interface{},reflect配合空接口,讓原本是靜态類型的go具備了很多動态類型語言的特征。 另外,雖然反射大大增加了go語言的靈活性,但要完全掌握它的原理和使用也還是有一點難度的。
go的reflect庫有兩個重要的類型:
reflect.Type
reflect.Value
Type,Value分别對應對象的類型和值資料
還有兩個重要的函數:
reflect.TypeOf(i interface{}) Type
reflect.TypeOf()傳回值的類型就是reflect.Type。
reflect.ValueOf(i interface{}) Value
reflect.ValueIOf()傳回值的類型就是reflect.Value
因為reflect.Typeof的參數是空接口類型,是以可以接收任意類型的資料。 TypeOf()的傳回值是這個接口類型對應的reflect.Type對象。通過Type提供的一些方法,就可以獲得這個接口實際的靜态類型。
1import (
2 "fmt"
3 "reflect"
4)
5
6type Foo struct {
7 X string
8 Y int
9}
10
11func main() {
12 var i int = 123
13 var f float32 = 1.23
14 var l []string = []string{"a", "b", "c"}
15
16 fmt.Println(reflect.TypeOf(i)) //int
17 fmt.Println(reflect.TypeOf(f)) //float32
18 fmt.Println(reflect.TypeOf(l)) //[]string
19
20 var foo Foo
21 fmt.Println(reflect.TypeOf(foo)) //main.Foo
22
23}
檢視reflect包的源代碼可以看到,reflect.Type的定義如下:
1type Type interface {
2 Align() int
3 FieldAlign() int
4 Method(int) Method
5 MethodByName(string) (Method, bool)
6 NumMethod() int
7 Name() string
8 String() string
9 Elem() Type
10 Field(i int) StructField
11 FieldByName(name string) (StructField, bool)
12 Len() int
13 .....
14}
15
可見reflect.Type是一個接口類型的對象,這個接口包含了很多方法,像
Name()
,
Field()
Method()
等,下面再通過執行個體來了解幾個比較重要的方法。
1type Foo struct {
2 X string
3 Y int
4}
5
6func (f Foo) do() {
7 fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)
8
9}
10
11func main() {
12 var s string = "abc"
13 fmt.Println(reflect.TypeOf(s).String()) //string
14 fmt.Println(reflect.TypeOf(s).Name()) //string
15
16 var f Foo
17 typ := reflect.TypeOf(f)
18 fmt.Println(typ.String()) //main.Foo
19 fmt.Println(typ.Name()) //Foo ,傳回結構體的名字
20
21}
上面的例子可見,通過Type.String(),Type.Name()方法就可以獲得接口對應的靜态類型。 下面幾個方法,顯示了Type的更多功能,特别是對于結構體對象而言。
Field相關的方法
1var f Foo
2typ := reflect.TypeOf(f)
3for i := 0; i < typ.NumField(); i++ {
4 field := typ.Field(i)
5 fmt.Printf("%s type is :%s\n", field.Name, field.Type)
6}
7
8//x type is :string
9//y type is :int
10
11field2, _ := typ.FieldByName("x") //等價于typ.Field(0),傳回的也是StructField對象
12fmt.Println(field2.Name) // x
Type的Field是一個StructFiled對象:
1type StructField struct {
2 Name string
3 PkgPath string
4
5 Type Type // field type
6 Tag StructTag // field tag string
7 Offset uintptr // offset within struct, in bytes
8 Index []int // index sequence for Type.FieldByIndex
9 Anonymous bool // is an embedded field
10}
Method相關的方法
1var f Foo
2typ := reflect.TypeOf(f)
3
4fmt.Println(typ.NumMethod()) //1, Foo 方法的個數
5m := typ.Method(0)
6fmt.Println(m.Name) //do
7fmt.Println(m.Type) //func(main.Foo)
8fmt.Println(m.Func) //<func(main.Foo) Value>, 這個傳回的是reflect.Value對象,後面再講
Kind
Kind方法Type和Value都有,它傳回的是對象的基本類型,例如int,bool,slice等,而不是靜态類型。
1var f = Foo{}
2typ := reflect.TypeOf(f)
3fmt.Println(typ) //main.Foo
4fmt.Println(typ.Kind()) //struct
5
6var f2 = &Foo{}
7typ2 := reflect.TypeOf(f2)
8fmt.Println(typ2) //*main.Foo
9fmt.Println(typ2.Kind()) //ptr
kind()的傳回值如下:
1const (
2 Invalid Kind = iota
3 Bool
4 Int
5 Int8
6 Int16
7 Int32
8 Int64
9 Uint
10 Uint8
11 Uint16
12 Uint32
13 Uint64
14 Uintptr
15 Float32
16 Float64
17 Complex64
18 Complex128
19 Array
20 Chan
21 Func
22 Interface
23 Map
24 Ptr
25 Slice
26 String
27 Struct
28 UnsafePointer
29)
reflect.ValueOf()的傳回值類型為reflect.Value,它實作了interface{}參數到reflect.Value的反射
1type Foo struct {
2 X string
3 Y int
4}
5
6func (f Foo) do() {
7 fmt.Printf("X is: %s, Y is: %d", f.X, f.Y)
8
9}
10
11
12func main() {
13 var i int = 123
14 var f = Foo{"abc", 123}
15 var s = "abc"
16 fmt.Println(reflect.ValueOf(i)) //<int Value>
17 fmt.Println(reflect.ValueOf(f)) //<main.Foo Value>
18 fmt.Println(reflect.ValueOf(s)) //abc
19
20 //Value.String()方法對string類型的資料做了特殊處理,會直接傳回字元串的值。
21 //其它類型對象傳回的格式都是"<Type% Value>"
22
23}
reflact.Value對象可以通過調用Interface()方法,再反射回interface{}對象
1 reflect.ValueOf() Interface()
2interface{} ---------------------> reflect.Value -------------------> interface{}
3
4var i int = 123
5fmt.Println(reflect.Valueof(i).Interface()) //123
6
7var f = Foo{"abc", 123}
8fmt.Println(f) //{abc 123}
9fmt.Println(reflect.ValueOf(f).Interface() == f) //true
10fmt.Println(reflect.ValueOf(f).Interface()) //{abc 123}
Value的Field方法
和Type的Filed方法不一樣,Type.Field()傳回的是StructFiled對象,有Name,Type等屬性,Value.Field()傳回的還是一個Value對象。
1var foo = Foo{"abc", 123}
2
3val := reflect.ValueOf(foo)
4fmt.Println(val.FieldByName("y")) //<int Value> interface.Value對象
5
6typ := reflect.Typeof(foo)
7fmt.Println(typ.FieldByName("y")) //{ <nil> 0 [] false} false StructField對象
1func main() {
2 var f = Foo{"abc", 123}
3 rv := reflect.ValueOf(f)
4 rt := reflect.TypeOf(f)
5 for i := 0; i < rv.NumField(); i++ {
6 fv := rv.Field(i)
7 ft := rt.Field(i)
8 fmt.Printf("%s type is :%s ,value is %v\n", ft.Name, fv.Type(), fv.Interface())
9 }
10}
11
12//X type is :string ,value is abc
13//Y type is :int ,value is 123
設定Value的值
要設定reflect.Value的值還頗費周折,不能直接對Value進行指派操作
1var s = "abc"
2fv := reflect.ValueOf(s)
3fmt.Println(fv.CanSet()) //false
4// fv.SetString("edf") //panic
5
6fv2 := reflect.ValueOf(&s)
7fmt.Println(fv2.CanSet()) //false
8// fv2.SetString("edf") //panic
relect.Value是字元s的一個反射對象,是不能直接對它進行指派操作的。 要對s進行指派,需要先拿到s的指針對應的reflect.Value,然後通過Value.Elem()再對應到s,然後才能指派操作。 這個地方是相當拗口啊:(
1func main() {
2 var i int = 123
3 fv := reflect.ValueOf(i)
4 fe := reflect.ValueOf(&i).Elem() //必須是指針的Value才能調用Elem
5 fmt.Println(fe) //<int Value>
6 fmt.Println(fv) //<int Value>
7 fmt.Println(fv == fe) //false
8
9 fmt.Println(fe.CanSet()) //true
10 fe.SetInt(456)
11 fmt.Println(i) //456
12
13}
Method
這個是reflect一個比較經典的使用場景,在知道對象方法名的情況下,調用對象的方法。
1type Foo struct {
2 X string
3 Y int
4}
5
6func (f Foo) Do() {
7 fmt.Printf("X is: %s, Y is: %d\n", f.X, f.Y)
8
9}
10
11func main() {
12 var foo = &Foo{"abc", 123}
13 reflect.ValueOf(foo).MethodByName("Do").Call([]reflect.Value{})
14
15}
16
17//方法名Do必須是大寫的,否則會抛異常
reflect整體不是很好了解,如果要進一步掌握如何使用,以及在什麼場景下用,建議看一些開源庫的代碼,來了解reflect的使用。下面幾個庫都大量使用了reflect,供參考:
web.go
redigo
原文釋出時間為:2018-06-25
本文來自雲栖社群合作夥伴“
Golang語言社群”,了解相關資訊可以關注“
”。