Go中也提供了反射機制,與Java同樣Go的反射也是在運行時獲取對象的相關信息,更新對象內部狀態;Golang經過反射能夠獲取對象類型、字段類型與值、調用struct實例方法、更新實例值等;
Go關於反射相關的對象、函數都在reflect包中最主要的兩個爲:Type與Value;
Go提供了下面兩個函數,這兩個是Go反射的核心;
reflect.TypeOf 返回目標對象的類型
reflect.ValueOf 返回值目標對象的值json
t:=1 fmt.Println(reflect.TypeOf(t), reflect.ValueOf(t)) 輸出:int 1
type Demo struct { Id int Name string } func (d *Demo) Back() { fmt.Println("調用方法 Back") } func (d *Demo) Add(a, b int) int { return a + b }
d := &Demo{Id: 2, Name: "test"} getValue(d) 輸出: Id : 2 Name : test func getValue(v interface{}) { t := reflect.TypeOf(v) o := reflect.ValueOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() //獲取類型指針中的類型 } if o.Kind() == reflect.Ptr { o = o.Elem() //獲取值地址中的值 } num := t.NumField() //獲取字段個數 for i := 0; i < num; i++ { f := o.Field(i) //獲取字段的值 fieldName := t.Field(i).Name //獲取字段名稱 switch f.Kind() { case reflect.Int: fmt.Println(fieldName, ": ", f.Int()) case reflect.String: fmt.Println(fieldName, ": ", f.String()) default: fmt.Println("類型不不支持") } } }
對於引用類型使用reflect.TypeOf返回的是該類型的指針,reflect.ValueOf返回的是該類型的值地址;因此對於引用類型都要的相關操做都要調用Elem()函數獲取真實的類型與值;
調用Type或Value對象的NumField函數獲取結構體的字段個數
調用Value對象的Field(i) 可獲取字段的值;
調用Value對象的Kind()函數可獲取字段的類型;
該Value對應於哪一種類型調用對應的函數便可獲取獲得相應的值,如類型不一致將拋出:panic: reflect: call of reflect.Value.Xxx on int Value;函數
d := new(Demo) setValue(d) fmt.Println(d) 輸出:&{88 Test} func setValue(v interface{}) { t := reflect.TypeOf(v) o := reflect.ValueOf(v) if t.Kind() == reflect.Ptr { t = t.Elem() } if o.Kind() == reflect.Ptr { o = o.Elem() } num := t.NumField() for i := 0; i < num; i++ { f := o.Field(i) switch f.Kind() { case reflect.Int: f.SetInt(88) //往該字段設值 case reflect.String: f.SetString("Test") /往該字段設值 default: fmt.Println("類型不支持") } } }
修改字段值與獲取值同樣,類型必定要一致,如不一致將拋異常,如int類型卻調用SetString設值:panic: reflect: call of reflect.Value.SetString on int Value;指針
d := new(Demo) callMethod(d) 輸出:調用方法 Back func callMethod(v interface{}) { o := reflect.ValueOf(v) o.MethodByName("Back").Call(nil) }
調用MethodByName根據名稱獲取方法,Call調用該方法;code
d := new(Demo) callMethodParam (d) 輸出:3 func callMethodParam(p interface{}) { o := reflect.ValueOf(p) args:=[]reflect.Value{reflect.ValueOf(1), reflect.ValueOf(2)} v:= o.MethodByName("Add").Call(args) fmt.Println(v[0]) }
比較經常使用的方法還有:
使用名字獲取結構體的成員
Refletct.ValueOf(*e).FieldByName(「Name」)
獲取結構體成員的json標記信息
reflect.TypeOf(s) . Field(0).Tag.Get(「key」)對象
Golang的反射也遵循Go語言規則,反射沒法修改結構體中的私有對象,沒法調用私有私有方法,可訪問私有成員,修改私有成員將會拋出reflect.flag.mustBeAssignable異常;blog
文章首發地址:Solinx
https://mp.weixin.qq.com/s/W0UVbFxMMeXA5HuNNZlK-Aget