17.3.1 基本介紹
1) 反射能夠在運行時 動態獲取變量的各類信息, 好比變量的類型(type),類別(kind)
2) 若是是結構體變量,還能夠獲取到結構體自己的信息(包括結構體的 字段、 方法)
3) 經過反射,能夠修改變量的值,能夠調用關聯的方法。
4) 使用反射,須要 import (「reflect」)json
1) reflect.TypeOf(變量名),獲取變量的類型,返回reflect.Typeapp
2) reflect.Value(變量名),獲取變量的值,返回reflect.Value(是一個結構體類型)
3) 變量、interface{} 和 reflect.Value 是能夠相互轉換的,這點在實際開發中,會常常使用到函數
請編寫一個案例,演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操做指針
代碼演示,見下面的表格:
請編寫一個案例,演示對(結構體類型、interface{}、reflect.Value)進行反射的基本操做code
package main import ( "reflect" "fmt" ) //專門演示反射 func reflectTest01(b interface{}) { //經過反射獲取的傳入的變量的 type , kind, 值 //1. 先獲取到 reflect.Type rTyp := reflect.TypeOf(b) fmt.Println("rType=", rTyp) //2. 獲取到 reflect.Value rVal := reflect.ValueOf(b) n2 := 2 + rVal.Int() //n3 := rVal.Float() fmt.Println("n2=", n2) //fmt.Println("n3=", n3) fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal) fmt.Printf("rVal=%v rTyp type=%T\n", rVal, rTyp) //下面咱們將 rVal 轉成 interface{} iV := rVal.Interface() //將 interface{} 經過斷言轉成須要的類型 num2 := iV.(int) fmt.Println("num2=", num2) } //專門演示反射[對結構體的反射] func reflectTest02(b interface{}) { //經過反射獲取的傳入的變量的 type , kind, 值 //1. 先獲取到 reflect.Type rTyp := reflect.TypeOf(b) fmt.Println("rType=", rTyp) //2. 獲取到 reflect.Value rVal := reflect.ValueOf(b) //3. 獲取 變量對應的Kind //(1) rVal.Kind() ==> kind1 := rVal.Kind() //(2) rTyp.Kind() ==> kind2 := rTyp.Kind() fmt.Printf("kind =%v kind=%v\n", kind1, kind2) //下面咱們將 rVal 轉成 interface{} iV := rVal.Interface() fmt.Printf("iv=%v iv type=%T \n", iV, iV) //將 interface{} 經過斷言轉成須要的類型 //這裏,咱們就簡單使用了一帶檢測的類型斷言. //同窗們可使用 swtich 的斷言形式來作的更加的靈活 stu, ok := iV.(Student) if ok { fmt.Printf("stu.Name=%v\n", stu.Name) } } type Student struct { Name string Age int } type Monster struct { Name string Age int } func main() { //請編寫一個案例, //演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操做 //1. 先定義一個int var num int = 100 reflectTest01(num) //2. 定義一個Student的實例 stu := Student{ Name : "tom", Age : 20, } reflectTest02(stu) }
1) reflect.Value.Kind,獲取變量的類別,返回的是一個常量
2) Type 和 Kind 的區別:排序
Type 是類型, Kind 是類別, Type 和 Kind 多是相同的,也 多是不一樣的. 好比: var num int = 10 num 的 Type 是 int , Kind 也是 int 好比: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct
3) 經過反射可讓變量在interface{}和reflect.Value之間互相轉換。
4) 經過反射獲取的變量的值,要求數據類型配,好比x是int,那麼就應該用reflect.Value.int(),而不能用其餘,不然會報panic的錯誤。
5) 經過反射的來修改變量, 注意當使用 SetXxx 方法來設置須要經過對應的指針類型來完成, 這樣才能改變傳入的變量的值, 同時須要使用到 reflect.Value.Elem()方法接口
package main import ( "reflect" "fmt" ) //經過反射,修改, // num int 的值 // 修改 student的值 func reflect01(b interface{}) { //2. 獲取到 reflect.Value rVal := reflect.ValueOf(b) // 看看 rVal的Kind是 fmt.Printf("rVal kind=%v\n", rVal.Kind()) //3. rVal //Elem返回v持有的接口保管的值的Value封裝,或者v持有的指針指向的值的Value封裝 rVal.Elem().SetInt(20) } func main() { var num int = 10 reflect01(&num) fmt.Println("num=", num) // 20 //你能夠這樣理解rVal.Elem() // num := 9 // ptr *int = &num // num2 := *ptr //=== 相似 rVal.Elem() }
package main import ( "fmt" "reflect" ) func main() { var str string = "tom" //ok fs := reflect.ValueOf(&str) //ok fs -> string fs.Elem().SetString("jack") //ok fmt.Printf("%v\n", str) // jack }
1) 使用 反射來遍歷結構體的字段, 調用結構體的方法,並 獲取結構體標籤的值
2) 使用反射的方式來獲取結構體的 tag 標籤, 遍歷字段的值,修改字段值,調用結構體方法(要求:經過傳遞地址的方式完成, 在前面案例上修改便可)開發
3) 定義了兩個函數 test1 和 test2,定義一個適配器函數用做統一處理接口(略,用反射實現便可)
4) 使用反射操做任意結構體類型:
5) 使用反射建立並操做結構體string
package main import ( "fmt" "reflect" ) //定義了一個Monster結構體 type Monster struct { Name string `json:"name"` Age int `json:"monster_age"` Score float32 `json:"成績"` Sex string } //方法,返回兩個數的和 func (s Monster) GetSum(n1, n2 int) int { return n1 + n2 } //方法, 接收四個值,給s賦值 func (s Monster) Set(name string, age int, score float32, sex string) { s.Name = name s.Age = age s.Score = score s.Sex = sex } //方法,顯示s的值 func (s Monster) Print() { fmt.Println("---start~----") fmt.Println(s) fmt.Println("---end~----") } func TestStruct(a interface{}) { //獲取reflect.Type 類型 typ := reflect.TypeOf(a) //獲取reflect.Value 類型 val := reflect.ValueOf(a) //獲取到a對應的類別 kd := val.Kind() //若是傳入的不是struct,就退出 if kd != reflect.Struct { fmt.Println("expect struct") return } //獲取到該結構體有幾個字段 num := val.NumField() fmt.Printf("struct has %d fields\n", num) //4 //變量結構體的全部字段 for i := 0; i < num; i++ { fmt.Printf("Field %d: 值爲=%v\n", i, val.Field(i)) //獲取到struct標籤, 注意須要經過reflect.Type來獲取tag標籤的值 tagVal := typ.Field(i).Tag.Get("json") //若是該字段於tag標籤就顯示,不然就不顯示 if tagVal != "" { fmt.Printf("Field %d: tag爲=%v\n", i, tagVal) } } //獲取到該結構體有多少個方法 numOfMethod := val.NumMethod() fmt.Printf("struct has %d methods\n", numOfMethod) //var params []reflect.Value //方法的排序默認是按照 函數名的排序(ASCII碼) val.Method(1).Call(nil) //獲取到第二個方法。調用它 //調用結構體的第1個方法Method(0) var params []reflect.Value //聲明瞭 []reflect.Value params = append(params, reflect.ValueOf(10)) params = append(params, reflect.ValueOf(40)) res := val.Method(0).Call(params) //傳入的參數是 []reflect.Value, 返回[]reflect.Value fmt.Println("res=", res[0].Int()) //返回結果, 返回的結果是 []reflect.Value*/ } func main() { //建立了一個Monster實例 var a Monster = Monster{ Name: "黃鼠狼精", Age: 400, Score: 30.8, } //將Monster實例傳遞給TestStruct函數 TestStruct(a) }
package main import ( "fmt" ) func main() { var num int num = 9 //ok //常量聲明的時候,必須賦值。 const tax int = 0 //常量是不能修改 //tax = 10 fmt.Println(num, tax) //常量只能修飾bool、數值類型(int, float系列)、string 類型 //fmt.Println(b) const ( a = iota b c d ) fmt.Println(a, b, c, d)//0 1 2 3 }