反射基本介紹:json
(1)反射能夠在運行時動態獲取變量的各類信息,好比變量的類型、類別;app
(2)若是是結構體變量,還能夠獲取結構體自己的信息(包括結構體字段、方法);函數
(3)經過反射,能夠修改變量的值,能夠調用關聯的方法;spa
(4)使用反射,須要Import "reflect:"3d
變量--空接口--relect.Value之間的轉換關係:指針
int變量與反射變量之間的轉換:code
package main import ( "fmt" "reflect" ) func reflectTest(b interface{}) { rTyp := reflect.TypeOf(b) fmt.Printf("rTyp=%v,type=%T\n", rTyp, rTyp) rVal := reflect.ValueOf(b) fmt.Printf("rVal=%v,type=%T\n", rVal, rVal) iVal := rVal.Interface() num2 := iVal.(int) fmt.Printf("num2=%v,type=%T\n", num2, num2) } func main() { var num int = 100 reflectTest(num) }
結構體變量與反射變量之間的轉換:blog
type student struct { name string age int } func reflectTest02(b interface{}) { rTyp := reflect.TypeOf(b) fmt.Printf("rTyp=%v,type=%T\n", rTyp, rTyp) rVal := reflect.ValueOf(b) fmt.Printf("rVal=%v,type=%T\n", rVal, rVal) iVal := rVal.Interface() stu2 := iVal.(student) fmt.Printf("stu2=%v,type=%T\n", stu2, stu2) fmt.Printf("name:%v,age:%v", stu2.name, stu2.age) } func main() { stu := student{ name: "tom", age: 20, } reflectTest02(stu) }
反射注意事項和使用細節:
(1)reflect.Vale.Kind,獲取變量的類別,返回的是一個常量;排序
(2)Type是類型,Kind是類別,Type和Kind多是相同的,也多是不一樣的;接口
好比:var num int=10的type是Int,Kind也是int
var stu Student, stu的rype是pkg1.Student,Kind是struct
(3)經過反射介意讓變量在interface{}和reflect.Value之間相互轉換;
(4)使用反射的方式來獲取變量的值,要求數據類型匹配;
(5)經過反射來修改變量,注意當使用SetXxx方法來設置須要經過對指針類型來完成,這樣才能改變傳入變量的值,同時須要使用到reflect.Value.Elem()方法;即rVal.Elem().SetInt(20)
具體實踐;
使用反射來遍歷結構體的字段,並調用結構體的方法,並獲取結構體標籤的值。
package main import ( "fmt" "reflect" ) type monster struct { Name string `json:"name"` Age int `json:"monster_age"` Score float32 Sex string } func (m monster) Print() { fmt.Println("----strat----") fmt.Println() fmt.Println("----end----") } func (m monster) GetSum(n1, n2 int) int { return n1 + n2 } func (m monster) Set(name string, age int, score float32, sex string) { m.Name = name m.Age = age m.Score = score m.Sex = sex } func testStruct(b interface{}) { //得到類型 rtyp := reflect.TypeOf(b) //得到值 rval := reflect.ValueOf(b) //得到類別 kd := rval.Kind() //若是kd不是指針類型且rval.Elem()不是結構體類別 if kd != reflect.Ptr && rval.Elem().Kind() == reflect.Struct { fmt.Println("expect struct") return } //獲取結構體有幾個字段 num := rval.Elem().NumField() fmt.Printf("struct has %d fields\n", num) //遍歷每一個字段 for i := 0; i < num; i++ { //輸出該字段的值 fmt.Printf("field %d:值爲=%v\n", i, rval.Elem().Field(i)) //獲取該字段的json標籤 tagval := rtyp.Elem().Field(i).Tag.Get("json") //若是標籤不爲空,則打印出標籤 if tagval != "" { fmt.Printf("field %d:tag爲=%v\n", i, tagval) } } rval.Elem().Field(0).SetString("孫悟空") //獲取結構體有幾個方法 numOfMethod := rval.Elem().NumMethod() fmt.Printf("struct has %d methods\n", numOfMethod) //獲取第二個方法,函數的順序是按首字母的ACSII進行排序的,因此這裏第二個方法是Print rval.Elem().Method(1).Call(nil) //定義一個reflect.Value切片 var params []reflect.Value //添加兩個整型 params = append(params, reflect.ValueOf(10)) params = append(params, reflect.ValueOf(40)) //調用第一個方法,並傳入參數(注意參數類型是reflect.Value切片) res := rval.Elem().Method(0).Call(params) //注意res是一個切片,裏面只有一個值 fmt.Println("res=", res[0].Int()) } func main() { var a monster = monster{ Name: "tom", Age: 400, Score: 20.9, } testStruct(&a) fmt.Println(a) }
要在反射中修改結構體的字段的值,必須傳入的是地址,而且在反射中使用.Elem().SetXxx()
具體實踐2:經過反射完成適配器
func main() { test1 := func(v1 int, v2 int) { fmt.Printf("v1=%d,v2=%d\n", v1, v2) } test2 := func(v1 int, v2 int, s string) { fmt.Printf("v1=%d,v2=%d,s=%s", v1, v2, s) } bridge := func(call interface{}, args ...interface{}) { //獲取參數長度 n := len(args) //定義參數切片 params := make([]reflect.Value, n) //將參數存入到切片中 for i := 0; i < n; i++ { params[i] = reflect.ValueOf(args[i]) } // for i := 0; i < n; i++ { // fmt.Println(args[i]) // } //獲取函數名 function := reflect.ValueOf(call) //將參數傳給函數,並調用函數 function.Call(params) } bridge(test1, 1, 2) bridge(test2, 1, 2, "test2") }
運行結果:
具體實踐3:經過反射操做任意結構體類型
type user struct { UserID string Name string } func testReflectStruct() { var ( model *user sv reflect.Value ) model = &user{} sv = reflect.ValueOf(model) //這裏是指針類別 fmt.Println(sv.Kind().String()) sv = sv.Elem() //這裏是結構體類別 fmt.Println(sv.Kind().String()) sv.FieldByName("UserID").SetString("1") sv.FieldByName("Name").SetString("tom") fmt.Println(model) }