1. Definition of reflection
It's a great source of conflict ~ (quoted from the official blog)
Reflection refers to dynamically accessing and modifying the structure and members of objects of any Type at runtime. The reflection package is provided in go language to provide the function of reflection. Each variable has two properties: Type and Value
Reflection can be self describing and self controlling
For example, python reflection: execute functions according to strings and import packages according to strings
Go is a static language. Reflection is a mechanism provided by go. You can do the following things when you don't know the type at compile time
- Update variable
- View values at runtime
- Call method
- Operate on their layout
Two classic scenes using reflection
-
The function you wrote doesn't know what the type passed to you is. It may not be agreed, or there may be many types passed in
-
You want to decide which function to call (call the method according to the string) and execute the function dynamically through the user's input
2. Basic data type of reflection
3,Type
reflect.Type is an interface type used to obtain information related to variable types. You can obtain the type information of a variable through the reflect.TypeOf function
Source code go/src/reflect/type.go
type Type interface { Align() int FieldAlign() int Method(int) Method // The i th method MethodByName(string) (Method, bool) // Get method by name NumMethod() int // Number of methods Name() string // Get structure name PkgPath() string // Package path Size() uintptr // Size of memory occupied String() string // Get string representation Kind() Kind // data type Implements(u Type) bool // Determine whether an interface is implemented AssignableTo(u Type) bool // Can it be assigned to another type ConvertibleTo(u Type) bool // Can I convert to another type Comparable() bool Bits() int ChanDir() ChanDir IsVariadic() bool Elem() Type // Resolve pointer (change pointer type to normal type) Field(i int) StructField // Member i FieldByIndex(index []int) StructField // Get nested members according to index path FieldByName(name string) (StructField, bool) // Get members by name FieldByNameFunc(match func(string) bool) (StructField, bool) In(i int) Type Key() Type Len() int // Length of container NumField() int NumIn() int // Number of output parameters NumOut() int // Returns the number of parameters Out(i int) Type common() *rtype uncommon() *uncommonType }
4,Value
reflect.Value is a structure type, which is used to obtain the information of variable value. The value information of modifying the original data type (a variable) can be obtained through the reflect.ValueOf function
Source code go/src/reflect/value.go
type Value struct { // Data type represented typ *rtype // Pointer to raw data ptr unsafe.Pointer }
5. Three laws of reflection
The interface type has a value and type pair, and the reflection is to check the value and type pair of the interface
Specifically, Go provides one set of methods to extract the value of the interface and another set of methods to extract the type of the interface
- reflect.Type provides a set of interface processing interface types, that is, value, type in type
- reflect.Value provides a set of interface processing interface values, i.e. value, value in type
5.1 first law of reflection
The first law of reflection: reflection can convert interface type variables into reflective objects
How to get the value and type of a variable through reflection
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 t := reflect.TypeOf(x) //t is reflext.Type fmt.Println("type:", t) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) v := reflect.ValueOf(x) //v is reflext.Value fmt.Println("value:", v) }
Program output
type: float64 kind is float64: true value: 3.4
Reflection is for interface type variables, where the parameters accepted by TypeOf() and ValueOf() are of interface {} type, that is, the x value is transferred into interface
5.2 second law of reflection
The second law of reflection: reflection can restore a reflected object to an interface object
The reason why it is called 'reflection', reflection objects and interface objects can be transformed into each other
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 v := reflect.ValueOf(x) //v is reflext.Value var y float64 = v.Interface().(float64) fmt.Println("value:", y) }
Object x is converted to reflection object v, and V is converted to interface object through Interface(). The interface object obtains the value of float64 type through. (float64) type assertion
5.3 the third law of reflection
The third law of reflection: the reflection object can be modified, and the value value must be settable
Reflection allows you to convert an interface type variable to a reflection object, which you can use to set the value it holds
package main import ( "reflect" ) func main() { var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) // Error: will panic. }
panic appears by setting a new value for the reflection object v
panic: reflect: reflect.Value.SetFloat using unaddressable value
The reason for the error is that v is not modifiable.
Whether the reflection object can be modified depends on its stored value. Recall whether the function passes a value or an address when passing parameters, it is not difficult to understand why the above example failed.
In the above example, the value of X passed into the reflect.ValueOf() function is actually the value of X, not x itself. That is, modifying the value of v cannot affect x, that is, it is an invalid modification, so golang will report an error
When you think of this, you can understand that if you use the address of x when building v, you can modify it, but at this time, v represents the pointer address. What we want to set is the content pointed to by the pointer, that is, * v is what we want to modify. Modify the value of x by v?
reflect.Value provides the Elem() method to get the value pointed to by the pointer
package main import ( "reflect" "fmt" ) func main() { var x float64 = 3.4 v := reflect.ValueOf(&x) v.Elem().SetFloat(7.1) fmt.Println("x :", v.Elem().Interface()) }
Output is:
x : 7.1
6. Common API s for reflection
6.1 get type
typeUser := reflect.TypeOf(&User{}) //Get Type through TypeOf() fmt.Println(typeUser) //*User fmt.Println(typeUser.Elem()) //User fmt.Println(typeUser.Name()) //Empty string fmt.Println(typeUser.Elem().Name()) //User, class name without package name fmt.Println(typeUser.Kind()) //ptr fmt.Println(typeUser.Elem().Kind()) //struct fmt.Println(typeUser.Kind() == reflect.Ptr) fmt.Println(typeUser.Elem().Kind() == reflect.Struct)
6.2 obtaining Field information
typeUser := reflect.TypeOf(User{}) //struct Type is required, but pointer Type is not allowed fieldNum := typeUser.NumField() //Number of member variables for i := 0; i < fieldNum; i++ { field := typeUser.Field(i) fmt.Printf("%d %s offset %d anonymous %t type %s exported %t json tag %s\n", i, field.Name, //Variable name field.Offset, //The string type occupies 16 bytes relative to the memory offset of the first address of the structure field.Anonymous, //Is it an anonymous member field.Type, //Data type, reflect.Type field.IsExported(), //Whether it is visible outside the package (i.e. whether it starts with a capital letter) field.Tag.Get("json")) //Get the tag defined in ` ` after the member variable //Field can be obtained through FieldByName if nameField, ok := typeUser.FieldByName("Name"); ok { fmt.Printf("Name is exported %t\n", nameField.IsExported()) } //Field can also be obtained according to FieldByIndex thirdField := typeUser.FieldByIndex([]int{2}) //The parameter is a slice because struct s are nested fmt.Printf("third field name %s\n", thirdField.Name) }
6.3 obtaining method information
typeUser := reflect.TypeOf(common.User{}) methodNum := typeUser.NumMethod() //Number of member methods. Methods where the receiver is a pointer [not included] for i := 0; i < methodNum; i++ { method := typeUser.Method(i) fmt.Println(method.Type) // The complete signature of the function will be output, and the input parameter will also take the structure itself as the input parameter, so the number of parameters is one more fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported()) } fmt.Println() if method, ok := typeUser.MethodByName("Examine2"); ok { // Get by method name fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported()) } typeUser2 := reflect.TypeOf(&common.User{}) methodNum = typeUser2.NumMethod() //Number of member methods. The method whose receiver is a pointer or value is included, that is, the method pointer implemented by the value is also implemented (otherwise, it does not hold) for i := 0; i < methodNum; i++ { method := typeUser2.Method(i) fmt.Printf("method name:%s ,type:%s, exported:%t\n", method.Name, method.Type, method.IsExported()) }
6.4 obtaining function information
typeFunc := reflect.TypeOf(Add) //Get function type fmt.Printf("is function type %t\n", typeFunc.Kind() == reflect.Func) argInNum := typeFunc.NumIn() //Number of input parameters argOutNum := typeFunc.NumOut() //Number of output parameters for i := 0; i < argInNum; i++ { argTyp := typeFunc.In(i) fmt.Printf("The first%d Type of input parameter%s\n", i, argTyp) } for i := 0; i < argOutNum; i++ { argTyp := typeFunc.Out(i) fmt.Printf("The first%d Types of output parameters%s\n", i, argTyp) }
6.5 assignment and conversion relationship
- Type1. Assignableto (type2) / / can the type represented by type1 be assigned to the type represented by type2
- Type1. Convertibleto (type2)) / / can the type represented by type1 be converted to the type represented by type2
- java reflection can obtain inheritance relationship, but go language does not support inheritance, so it must be of the same type to AssignableTo and ConvertibleTo
Example
u := reflect.TypeOf(User{}) t := reflect.TypeOf(Student{}) //User is nested inside Student u2 := reflect.TypeOf(User{}) //false false fmt.Println(t.AssignableTo(u)) //Can the type represented by t be assigned to the type represented by u fmt.Println(t.ConvertibleTo(u)) //Can the type represented by t be converted to the type represented by u //false false fmt.Println(u.AssignableTo(t)) fmt.Println(u.ConvertibleTo(t)) //true true fmt.Println(u.AssignableTo(u2)) fmt.Println(u.ConvertibleTo(u2))
6.6 whether the interface is implemented
//Get the interface type through reflect. Typeof ((* < interface >) (NIL)). Elem(). Because people is an interface and cannot create an instance, nil is forced to be of type * common.People typeOfPeople := reflect.TypeOf((*common.People)(nil)).Elem() // nil can be understood as an instance of the People pointer fmt.Printf("typeOfPeople kind is interface %t\n", typeOfPeople.Kind() == reflect.Interface) t1 := reflect.TypeOf(common.User{}) t2 := reflect.TypeOf(&common.User{}) //If the value type of User implements the interface, the pointer type also implements the interface; But not the other way around (try changing the receiver of Think to * User) fmt.Printf("t1 implements People interface %t\n", t1.Implements(typeOfPeople)) // false fmt.Printf("t2 implements People interface %t\n", t2.Implements(typeOfPeople)) // true
6.7 value and other types of interchanges
//Original type to Value iValue := reflect.ValueOf(1) sValue := reflect.ValueOf("hello") userPtrValue := reflect.ValueOf(&common.User{ Id: 7, Name: "Jackson", Weight: 65, Height: 1.68, }) fmt.Println(iValue) //1 fmt.Println(sValue) //hello fmt.Println(userPtrValue) //&{7 Jackson 65 1.68} //Value to Type iType := iValue.Type() sType := sValue.Type() userType := userPtrValue.Type() //Calling Kind() on Type and corresponding Value results in the same fmt.Println(iType.Kind() == reflect.Int, iValue.Kind() == reflect.Int, iType.Kind() == iValue.Kind()) //true true fmt.Println(sType.Kind() == reflect.String, sValue.Kind() == reflect.String, sType.Kind() == sValue.Kind()) //true true fmt.Println(userType.Kind() == reflect.Ptr, userPtrValue.Kind() == reflect.Ptr, userType.Kind() == userPtrValue.Kind()) //true true true //Pointer Value and non pointer Value are converted to each other userValue := userPtrValue.Elem() //Elem() pointer Value to non pointer Value fmt.Println(userValue.Kind(), userPtrValue.Kind()) //struct ptr userPtrValue3 := userValue.Addr() //Addr() non pointer Value to pointer Value fmt.Println(userValue.Kind(), userPtrValue3.Kind()) //struct ptr //Convert to original type //Convert the Value to interface {} through the Interface() function, and then convert it from interface {} to the original data type //Or directly call Int(), String(), etc. on Value in one step fmt.Printf("origin value iValue is %d %d\n", iValue.Interface().(int), iValue.Int()) fmt.Printf("origin value sValue is %s %s\n", sValue.Interface().(string), sValue.String()) user := userValue.Interface().(common.User) fmt.Printf("id=%d name=%s weight=%.2f height=%.2f\n", user.Id, user.Name, user.Weight, user.Height) user2 := userPtrValue.Interface().(*common.User) fmt.Printf("id=%d name=%s weight=%.2f height=%.2f\n", user2.Id, user2.Name, user2.Weight, user2.Height)
6.8 three cases of value judging null value
The pre declarations of pointer, channel, func, interface, map and slice are nil
var i interface{} //The interface does not point to a specific value v := reflect.ValueOf(i) fmt.Printf("v Holding value %t, type of v is Invalid %t\n", v.IsValid(), v.Kind() == reflect.Invalid) // false var user *common.User = nil v = reflect.ValueOf(user) //Value points to a nil if v.IsValid() { fmt.Printf("v The value held is nil %t\n", v.IsNil()) //Ensure IsValid() before calling IsNil(), otherwise panic / / true will occur } var u common.User //Just declare that the values inside are 0 values v = reflect.ValueOf(u) if v.IsValid() { fmt.Printf("v The value held is the 0 value of the corresponding type %t\n", v.IsZero()) //Ensure IsValid() before calling IsZero(), otherwise panic / / true will occur }
reference resources: https://go.dev/blog/laws-of-reflection
See you ~
Pay attention to the official account plus more dry cargo.