
1. 概述
Go語言提供了一種機制,能夠在運行時更新變量和檢查它們的值、調(diào)用它們的方法和它們支持的內(nèi)在操作,而不需要在編譯時就知道這些變量的具體類型。這種機制被稱為反射。反射也可以讓我們將類型本身作為第一類的值類型處理。
2. 反射類型對象
使用
reflect.TypeOf()函數(shù)獲取任意變量的類型對象reflect.Type**函數(shù)的源碼如下 : **
// TypeOf returns the reflection Type that represents the dynamic type of i. // If i is a nil interface value, TypeOf returns nil. func TypeOf(i interface{}) Type { eface := *(*emptyInterface)(unsafe.Pointer(&i)) return toType(eface.typ) }通過獲取的類型對象就能訪問原變量的類型信息
在類型信息中我們需要知道
類型(Type)和種類(Kind)的區(qū)別類型 Type : 通常指的是系統(tǒng)中的原生數(shù)據(jù)類型和使用
type關(guān)鍵字定義的類型,通過類型對象reflect.Type中的Name()方法獲取種類Kind : 指定的對象的根本種類,是更高一層的概括,通過
reflect.Type中的Kind()函數(shù)獲取
- Type的值和Kind的值可能相同,也可能不相同
package main
import (
"fmt"
"reflect"
)
type char string
type dogs struct {
}
func main() {
var c char
Str := "golang go.."
// 獲取C的類型對象
TypeOfC := reflect.TypeOf(c)
// Name() 獲取類型
// Kind() 獲取種類
fmt.Println("Type = ", TypeOfC.Name(), "Kind = ", TypeOfC.Kind()) // Type = char Kind = string
Golden := dogs{}
TypeOfGolden := reflect.TypeOf(Golden)
fmt.Println("Type = ", TypeOfGolden.Name(), "Kind = ", TypeOfGolden.Kind()) // Type = char Kind = string
TypeOfStr := reflect.TypeOf(Str)
fmt.Println("Type = ", TypeOfStr.Name(), "Kind = ", TypeOfStr.Kind()) //Type = string Kind = string
huntaway := &dogs{} // 指針變量
TypeOfHuntaway := reflect.TypeOf(huntaway)
// Go語言中所有的指針變量種類都是 `ptr`
// 指針變量的類型是此時是空
fmt.Println("Type = ", TypeOfHuntaway.Name(), "Kind = ", TypeOfHuntaway.Kind()) // Type = Kind = ptr
// 對指針獲取反射對象時,可以通過 reflect.Elem() 方法獲取這個指針指向的元素類
TypeOfHuntaway = TypeOfHuntaway.Elem()
fmt.Println("Type = ", TypeOfHuntaway.Name(), "Kind = ", TypeOfHuntaway.Kind()) // Type = dogs Kind = struct
}
go run main.go
Type = char Kind = string
Type = dogs Kind = struct
Type = string Kind = string
Type = Kind = ptr
Type = dogs Kind = struct
當(dāng)一個變量是結(jié)構(gòu)體實例的時候,怎么通過反射獲取類型信息呢?
通過 reflect 包中
reflect.type的Field()FieldByIndexFieldByNameFieldByNameFunc方法獲取的 StructField 結(jié)構(gòu)體有些什么內(nèi)容呢?// A StructField describes a single field in a struct. type StructField struct { // Name is the field name. Name string // PkgPath is the package path that qualifies a lower case (unexported) // field name. It is empty for upper case (exported) field names. // See https://golang.org/ref/spec#Uniqueness_of_identifiers PkgPath string Type Type // field type Tag StructTag // field tag string Offset uintptr // offset within struct, in bytes Index []int // index sequence for Type.FieldByIndex Anonymous bool // is an embedded field }
package main
import (
"fmt"
"reflect"
)
type dogs struct {
Name string `json:"name"`
Age int8
T int `json:"t" id:"99"`
}
func main(){
// 創(chuàng)建實例
Hachiko := dogs{Name:"Hachiko",Age:int8(2),T:66}
//獲取反射對象實例
typeD := reflect.TypeOf(Hachiko)
// NumField()函數(shù),返回結(jié)構(gòu)體成員的數(shù)量
for i:=0;i<typeD.NumField();i++{
// 獲取結(jié)構(gòu)體成員的類型
// Field()函數(shù) 根據(jù)索引返回結(jié)構(gòu)體對應(yīng)field的信息
typeOfField := typeD.Field(i)
// 輸出成員字段名稱和tag(標(biāo)簽信息),成員類型
fmt.Printf("%s , %v,%s\n",typeOfField.Name,typeOfField.Tag,typeOfField.Type)
}
// 通過結(jié)構(gòu)體字段名獲取其類型信息
// FieldByName()函數(shù),根據(jù)字段名返回字段信息
if fieldType,ok := typeD.FieldByName("T");ok{
fmt.Println(fieldType.Tag.Get("json"),fieldType.Tag.Get("id"))
}
}
go run main.go
Name , json:"name",string
Age , ,int8
T , json:"t" id:"99",int
t 99
3. 反射的值對象
反射可以動態(tài)的獲取或者設(shè)置變量的值
Go語言中使用reflect.Value獲取和設(shè)置變量的值
package main
import (
"fmt"
"reflect"
)
func main() {
var a int = 99
fmt.Printf("a ==> %T,%v\n", a, a)
// 使用reflect.ValueOf()函數(shù)獲取反射值對象
valueOfA := reflect.ValueOf(a)
// 獲取的反射值對象,再通過值對象的Interface()方法獲取原值
var b int = valueOfA.Interface().(int)
fmt.Printf("b ==> %T,%v\n", b, b)
// 反射對象的Int()方法獲取int64類型值,然后強制轉(zhuǎn)換成int32位
var c int32 = int32(valueOfA.Int())
fmt.Printf("c ==> %T,%v\n", c, c)
}
go run main.go
a ==> int,99
b ==> int,99
c ==> int32,99
package main
import (
"fmt"
"reflect"
)
type demo struct {
a int
b string
c bool
float64
d [5]int
}
func (d *demo) dF1() {
fmt.Println(d.a)
}
func (d *demo) dF2() {
fmt.Println(d.b)
}
func main() {
t := demo{99, "golang", true, 98.90, [5]int{1, 2, 3, 5, 6}}
// 獲取值對象
valueOfT := reflect.ValueOf(t)
// NumField()是獲取字段數(shù)量
fmt.Println(valueOfT.NumField()) // 5
// 獲取索引為1的字段
fieldOf1 := valueOfT.Field(1)
// 打印該值對象的類型
fmt.Println(fieldOf1.Type()) // string
// 通過字段名查找
fieldOfd := valueOfT.FieldByName("d")
fmt.Println(fieldOfd.Type()) // [5]int
}
go run main.go
5
string
[5]int
4. 反射修改值
reflect.Value也提供了修改版值的方法
package main
import (
"fmt"
"reflect"
)
func main() {
var age int8 = 99
// 獲取一個值對象
valueOfAge := reflect.ValueOf(&age)
// Elem() 對可尋值的元素獲取它的值
// Addr() 對可尋址的元素獲取它地址
// CanSet() bool 返回元素(值對象)是否能被設(shè)置
// CanAddr() bool 返回元素(值對象)是否能被尋址
valueOfAge = valueOfAge.Elem()
valueOfAgeAddr := valueOfAge.Addr()
fmt.Println(valueOfAge) // 99
fmt.Println(valueOfAgeAddr) // 0xc000054080
fmt.Println(valueOfAge.CanSet()) // true
fmt.Println(valueOfAge.CanAddr()) // true
// SetInt() 使用int64設(shè)置值
// SetUint() 使用uint64設(shè)置值
// SetFloat() 使用float64設(shè)置值
// SetBool() 使用bool設(shè)置值
// SetBytes() 設(shè)置字節(jié)數(shù)組[]bytes值
// SetString 設(shè)置字符串值
valueOfAge.SetInt(1)
fmt.Printf("age type= %T, value= %v",age,age)
}
go run main.go
99
0xc000054080
true
true
age type= int8, value= 15
通過類型創(chuàng)建類型實例
package main
import (
"fmt"
"reflect"
)
func main(){
var i int = 99
// 獲取反射類型對象
typeOfI := reflect.TypeOf(i)
// 根據(jù)反射類型對象創(chuàng)建類型實例
newI := reflect.New(typeOfI)
// 答應(yīng)類型和種類
fmt.Println(newI.Type(),newI.Kind()) //*int ptr
}
5. 綜合Demo
package main
import (
"fmt"
"log"
"reflect"
)
type Person struct {
Name string `json:"name"`
Age int `json:"age"`
Sex string `json:"sex"`
}
func (p Person) PersonSet(name string,age int,sex string){
p.Name = name
p.Age = age
p.Sex = sex
fmt.Println(p)
}
func (p Person) ShowPerson(){
fmt.Println(p)
}
func reflectOfStruct(a interface{}){
// 獲取reflect.Type類型
typeObj := reflect.TypeOf(a)
// 獲取reflect.Value類型
valueObj := reflect.ValueOf(a)
// 獲取Kind類別,下面兩種方法都能獲取
KindType := typeObj.Kind()
//KindValue := valueObj.Kind()
//fmt.Println(KindType)
//fmt.Println(KindValue)
if KindType != reflect.Struct{
log.Fatal("Kind is not error")
return
}
// 獲取字段數(shù)量
fieldsNum := valueObj.NumField()
for i:=0;i<fieldsNum;i++{
fmt.Printf("field %d %v\n",i,valueObj.Field(i))
// 獲取指定的標(biāo)簽值
tagValue := typeObj.Field(i).Tag.Get("json")
if tagValue != ""{
fmt.Printf("field %d tag = %v\n",i,tagValue)
}
}
// 獲取方法數(shù)量
MethodNum := valueObj.NumMethod()
fmt.Printf("has %d methods\n",MethodNum)
// 調(diào)用第一個方法
valueObj.Method(1).Call(nil)
// 對有參數(shù)的方法調(diào)用
var params []reflect.Value
params = append(params,reflect.ValueOf("lisi"))
params = append(params,reflect.ValueOf(88))
params = append(params,reflect.ValueOf("man"))
// 傳遞參數(shù),調(diào)用指定方法名的方法
valueObj.MethodByName("PersonSet").Call(params)
}
func main() {
var a Person = Person{
Name:"zhangsan",
Age:99,
}
// 調(diào)用函數(shù)
reflectOfStruct(a)
}
go run main.go
field 0 zhangsan
field 0 tag = name
field 1 99
field 1 tag = age
field 2
field 2 tag = sex
has 2 methods
{zhangsan 99 }
{lisi 88 man}