[TOC]
## 反射
### 原始類型-->接口-->反射類型

### Kind 反射的類型種類
```
type Users interface{
GetName()
}
type UsersPtr interface{
GetAge()
GetSex()
}
type Person struct {
Name string `json:"name" xml:"name1"`
age int `json:"age"`
gender int `json:"sex"`
}
func (p Person) GetName(){
fmt.Println("叫我小叮當啊")
}
func (p *Person) GetAge(){
fmt.Println(18)
}
func (p *Person) GetSex(){
fmt.Println(1)
}
person := reflect.TypeOf(Person{})
fmt.Println(person.Name())// Person
pK := person .Kind()
fmt.Println(pK) // struct
```
### reflect.Type 用于獲取任何表達式的類型
```
//返回type類型的變量
func TypeOf(i interface{}) Type
//Type 類型可調用的方法
type Type interface {
// 該類型內存分配大小(內存對齊單位子節)
Align() int
// 該類型作為結構體字段時內存分配大小(內存對齊單位子節)
FieldAlign() int
// 根據index in [0, NumMethod())獲取方法
Method(int) Method
// 根據方法名獲取方法
MethodByName(string) (Method, bool)
// 獲取所有可用方法數量
// 接口類型 獲取包含未導出方法
NumMethod() int
// 返回類型名,未定義則為空
Name() string
// 返回類型所在包名,未定義則為空
PkgPath() string
// 錯誤類型所需子節數 unsafe.Sizeof.
Size() uintptr
// 返回類型名稱字符串
String() string
// 返回此類型的kind類型
Kind() Kind
// 返回是否實現了u接口
Implements(u Type) bool
// 返回類型的值是否可分配給類型u
AssignableTo(u Type) bool
// 返回類型的值是否可以轉換為u類型
ConvertibleTo(u Type) bool
// 返回類型的值是否可對比
Comparable() bool
// Methods applicable only to some types, depending on Kind.
// The methods allowed for each kind are:
//
// Int*, Uint*, Float*, Complex*: Bits
// Array: Elem, Len
// Chan: ChanDir, Elem
// Func: In, NumIn, Out, NumOut, IsVariadic.
// Map: Key, Elem
// Ptr: Elem
// Slice: Elem
// Struct: Field, FieldByIndex, FieldByName, FieldByNameFunc, NumField
// Bits returns the size of the type in bits.
// It panics if the type's Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits() int
// Elem返回類型的元素類型
// 不是 Array, Chan, Map, Ptr, or Slice. panic
Elem() Type
// --------------- struct ------------------------
// 字段返回結構類型的第i個字段。
// 不是struct 或者下表越界 painc
Field(i int) StructField
// 字段返回嵌套結構類型的第i個字段。
// 不是struct 或者下表越界 painc
FieldByIndex(index []int) StructField
// 按名稱返回字段
FieldByName(name string) (StructField, bool)
// 按過濾方法返回匹配字段
FieldByNameFunc(match func(string) bool) (StructField, bool)
// 返回結構體字段總數
// 不是 Struct panic.
NumField() int
// --------------- struct ------------------------
// --------------- func --------------------
//返回函數類型第i的輸入參數
// 不是Func painc
In(i int) Type
// 返回方法輸入參數總數
// 不是Func painc
NumIn() int
// 返回函數輸出參數總數
// 不是Func painc
NumOut() int
// 返回函數類型第i的輸出參數
// 不是Func painc
Out(i int) Type
// 返回函數是否包含可變參數
// 不是Func painc
IsVariadic() bool
// --------------- func --------------------
// --------------- Map --------------------
// 返回map的key類型
// 不是Map panic
Key() Type
// --------------- Map --------------------
// --------------- Array --------------------
// 返回數組類型的長度
// 不是Array panic
Len() int
// --------------- Array --------------------
// --------------- chan --------------------
// 返回chan類型的方向,不是chan會panic
ChanDir() ChanDir
// --------------- chan --------------------
common() *rtype
uncommon() *uncommonType
}
```
#### 獲取Type類型
```
var x int
tInt := reflect.TypeOf(x)
fmt.Println(tInt) // int
var name string
tSring := reflect.TypeOf(name)
fmt.Println(tSring) // string
```
#### 指針Type -->非指針Type
```
ptrType = reflect.TypeOf(&Person{})
tType = reflect.TypeOf(Person{})
//通過Elem
fmt.Println(ptrType.Elem().Name(), tType.Name() ) // Person Person
```
#### 結構體成員變量
```
type StructField struct {
Name string // 字段名
PkgPath string // 字段路徑
Type Type // 字段反射類型對象
Tag StructTag // 字段的結構體標簽
Offset uintptr // 字段在結構體中的相對偏移
Index []int // Type.FieldByIndex中的返回的索引值
Anonymous bool // 是否為匿名字段
}
//只能用struct的Type,不能是指針的Type
person := reflect.TypeOf(Person{})
//獲取字段數量,包括全部結構體字段,
fieldNum := person.NumField()
fmt.Println(fieldNum)
for i:= 0;i< fieldNum;i++{
field:= person.Field(i)// 在這返回StructField 類型
fmt.Printf("字段名稱:%s 偏移量: %d 是否匿名:%t 類型:%s
包外是否可見:%t allTag:%s jsonTag: %s\n",
field.Name,
field.Offect,
field.Anonymous,
field.Type,
field.IsExported(),
field.Tag,
field.Tag.Get("json")
)
}
//可以通過FieldByName獲取Field
field,has := person.FieldByName("Name")
fmt.Println(field,has)
//{Name string json:"name" xml:"name1" 0 [0] false} true
field,has = person.FieldByName("age")
fmt.Println(field,has)
//{age main int json:"age" 16 [1] false} true
field,has = person.FieldByName("gender")
fmt.Println(field,has)
//{gender main int json:"sex" 24 [2] false} true
//不存在的字段
field,has = person.FieldByName("gender1")
fmt.Println(field,has)
//{ <nil> 0 [] false} false
```
#### 結構體成員方法
```
//結構體,只包含值的方法不包含指針的方法
person := reflect.TypeOf(&Person{})
//獲取方法數量
methodNum := person.NumMethod()
for i:=0;i<methodNum;i++{
method := person.Method(i)
fmt.Printf("method name:%s ,type:%s\n", method.Name, method.Type)
}
//method name:GetName ,type:func(main.Person)
//指針
//指針或值的方法都包含在內,也就是說值實現的方法指針也實現了對應方法
person1 := reflect.TypeOf(&Person{})
methodNum1 := person1.NumMethod()
for i:=0;i<methodNum1;i++{
method1 := person1.Method(i)
fmt.Printf("method name:%s ,type:%s\n", method1.Name, method1.Type)
}
//method name:GetAge ,type:func(*main.Person)
//method name:GetName ,type:func(*main.Person)
//method name:GetSex ,type:func(*main.Person)
```
#### 函數信息
```
func calc(a,b int) int{
return a + b
}
tFunc := reflect.TypeOf(calc)
fmt.Println(tFunc)//func(int, int) int
//獲取入參數量
inNum := tFunc.NumIn()
fmt.Println(inNum)//2
for i := 0; i < inNum; i++ {
argIn := tFunc.In(i)
fmt.Printf("第%d個輸入參數的類型%s\n", i, argIn)
//第0個輸入參數的類型int
}
//獲取出參數量
outNum := tFunc.NumOut()
fmt.Println(outNum )//1
for i := 0; i < outNum; i++ {
argOut := tFunc.In(i)
fmt.Printf("第%d個輸出參數的類型%s\n", i, argOut)
//第0個輸出參數的類型int
}
```
#### 是否實現了某個接口
```
//通過reflect.TypeOf((*<interface>)(nil)).Elem()獲得接口類型。
//因為People是個接口不能創建實例,所以把nil強制轉為*Users類型
typeOnUsers := reflect.TypeOf((*Users)(nil)).Elem()
fmt.Printf("typeOnUsers kind is interface %t\n", typeOnUsers.Kind() == reflect.Interface)
//typeOnUsers kind is interface true
//如果值類型實現了接口,則指針類型也實現了接口;反之不成立
u1 := reflect.TypeOf(Person{})
u2 := reflect.TypeOf(&Person{})
fmt.Printf("u1 implements Users interface %t\n", u1.Implements(typeOnUsers))
//u1 implements Users interface true
fmt.Printf("u2 implements Users interface %t\n", u2.Implements(typeOnUsers))
//u2 implements Users interface true
typeOnUsersPtr := reflect.TypeOf((*UsersPtr)(nil)).Elem()
fmt.Printf("u1 implements Users interface %t\n", u1.Implements(typeOnUsersPtr))
//u1 implements Users interface false
fmt.Printf("u2 implements Users interface %t\n", u2.Implements(typeOnUsersPtr))
//u2 implements Users interface true
```
### reflect.Value 用戶獲取任何表達式的反射值
```
//返回Value類型變量
func ValueOf(i interface{}) Value
//Value類型的結構體
type Value struct {
// 表示的值的類型
typ *rtype
// 指向原始數據的指針
ptr unsafe.Pointer
//標志位保存有關該值的元數據
flag
}
```
| 方法名 | 說 ?明 |
| --- | --- |
| Interface() interface {} | 將值以 interface{}?類型返回,可以通過類型斷言轉換為指定類型 |
| Int() int64 | 將值以 int 類型返回,所有有符號整型均可以此方式返回 |
| Uint() uint64 | 將值以 uint 類型返回,所有無符號整型均可以此方式返回 |
| Float() float64 | 將值以雙精度(float64)類型返回,所有浮點數(float32、float64)均可以此方式返回 |
| Bool() bool | 將值以 bool 類型返回 |
| Bytes() \[\]bytes | 將值以字節數組 \[\]bytes 類型返回 |
| String() string | 將值以字符串類型返回 |
```
// 返回value是否可以被尋址
func (v Value) CanAddr() bool
// 類似& value尋址返回包裝value
func (v Value) Addr() Value
// 返回value是否可以被修改 需要CanAddr
func (v Value) CanSet() bool
// 類似* value解引用后返回包裝value
// 需要prt interface
func (v Value) Elem() Value
// 方法調用
func (v Value) Call(in []Value) []Value
// 方法調用 可變參數使用數組傳入
func (v Value) CallSlice(in []Value) []Value
func (v Value) TryRecv() (x Value, ok bool)
func (v Value) Recv() (x Value, ok bool)
func (v Value) TrySend(x Value) bool
func (v Value) Send(x Value)
func (v Value) Close()
func (v Value) Field(i int) Value
func (v Value) FieldByIndex(index []int) Value
func (v Value) FieldByName(name string) Value
func (v Value) FieldByNameFunc(match func(string) bool) Value
func (v Value) Method(i int) Value
func (v Value) NumMethod() int
func (v Value) NumField() int
func (v Value) MethodByName(name string) Value
func (v Value) Index(i int) Value
func (v Value) Type() Type
func (v Value) Kind() Kind
func (v Value) Convert(t Type) Value
func (v Value) UnsafeAddr() uintptr
// 返回子節數組
func (v Value) Bytes() []byte
func (v Value) SetBytes(x []byte)
// 返回string類型
func (v Value) String() string
func (v Value) SetString(x string)
// 返回interface類型
func (v Value) CanInterface() bool
func (v Value) Interface() (i interface{})
func (v Value) InterfaceData() [2]uintptr
func (v Value) Set(x Value)
// 返回float64
func (v Value) Float() float64
func (v Value) SetFloat(x float64)
func (v Value) OverflowFloat(x float64) bool
// 返回int64
func (v Value) Int() int64
func (v Value) SetInt(x int64)
func (v Value) OverflowInt(x int64) bool
// 返回uint64
func (v Value) Uint() uint64
func (v Value) SetUint(x uint64)
func (v Value) OverflowUint(x uint64) bool
// 返回bool
func (v Value) Bool() bool
func (v Value) SetBool(x bool)
// 返回complex128
func (v Value) Complex() complex128
func (v Value) SetComplex(x complex128)
func (v Value) OverflowComplex(x complex128) bool
// map操作
func (v Value) SetMapIndex(key, elem Value)
func (v Value) MapIndex(key Value) Value
func (v Value) MapKeys() []Value
func (v Value) MapRange() *MapIter
// chan func interface map ptr slice 返回值是否位nil
func (v Value) IsNil() bool
// 返回value本身是否有效
func (v Value) IsValid() bool
// 返回value是否位該對應類型的零值
func (v Value) IsZero() bool
```
#### 獲取Value類型
```
x := 188
tInt := reflect.ValueOf(x)
fmt.Println(tInt,tInt.Type()) // 188
fmt.Println(tInt.Type()) // int value類型轉type
name := "叫我小叮當啊"
tSring := reflect.ValueOf(name)
fmt.Println(tSring,tSring.Type()) // 叫我小叮當啊
fmt.Println(tSring.Type()) //string value類型轉type
personPtrValue := reflect.ValueOf(&Person{
Name: "叫我小叮當啊",
age: 18,
gender: 1,
})
fmt.Println(personPtrValue) //&{叫我小叮當啊 18 1}
fmt.Println(personPtrValue.Type()) //Person value類型轉type
```
#### 指針Value和非指針Value互相轉換
```
personValue := personPtrValue.Elem() //Elem() 指針Value轉為非指針Value
fmt.Println(personValue .Kind(), personPtrValue.Kind()) //struct ptr
personPtrValueNew := personValue .Addr() //Addr() 非指針Value轉為指針Value
fmt.Println(personValue.Kind(), personPtrValueNew .Kind()) //struct ptr
```
#### Value 轉換到 原始類型
```
//通過interface()方法把value類型轉到interface{}類型,再類型斷言轉換,強制轉換
//或者直接value類型直接Int()或者其他方法直接轉換
x := 188
tInt := reflect.ValueOf(x)
fmt.Printf("%d %d", tInt.Interface().(int), tInt.Int())//188 188
name := "叫我小叮當啊"
tSring := reflect.ValueOf(name)
fmt.Printf("%s %s", tSring .Interface().(string), tSring .String())
personPtrValue := reflect.ValueOf(&Person{
Name: "叫我小叮當啊",
age: 18,
gender: 1,
})
p := personPtrValue.Interface().(*Person)//這塊用*主要是上面是指針
fmt.Println(p.Name,p.age,p.gender)//叫我小叮當啊 18 1
```
#### 空value判斷
```
var x interface{} = nil
xp := reflect.ValueOf(x)
if xp.IsValid() {
fmt.Println("xp is valid")
}else{
fmt.Println("xp not valid") //進入到這里
}
`x`的值設為nil,所以沒有具體的類型,因此`xp.IsValid()`函數返回的值是false,表示這個變量是無效的。
var x interface{} = 10
xps := reflect.ValueOf(x)
if xps.IsValid() {
fmt.Println("xps is valid") //進入到這里
fmt.Printf("xps 的值是0值嗎 %t\n", xps.IsZero())//xps 的值是0值嗎 false
}else{
fmt.Println("xps not valid")
}
`x`的值設為一個整數類型的值10.由于`x`是一個空接口類型,因此在將它傳遞給`reflect.ValueOf()`函數之后,變量`xps`的具體類型會被自動地識別為整數類型`int`
var person *Person = nil
p := reflect.ValueOf(person)
if p.IsValid() {
fmt.Println("person is valid") //進入到這里
fmt.Printf("p 的值是nil %t\n", p.IsNil())//p 的值是nil true
fmt.Printf("xps 的值是0值嗎 %t\n", p.IsZero())//xps 的值是0值嗎 true
}else{
fmt.Println("person not valid")
}
```
**謹記在調用類似IsZero(),IsNil()之前一定用IsValid()確認是否有效,否則會panic**
#### Value修改值
**要想修改原始數據的值,給ValueOf傳的必須是指針,而指針Value不能調用Set和FieldByName方法,所以得先通過Elem()轉為非指針Value。未導出成員的值不能通過反射進行修改。**
##### 修改原始值
```
var i int = 10
iInt := reflect.ValueOf(&i)
iInt.Elem().SetInt(8)
fmt.Println(i)// 8
var s string = "叫我小叮當啊"
iString := reflect.ValueOf(&s)
iString.Elem().SetString("叫我老叮當啊")
fmt.Println(s)// 叫我老叮當啊
```
**為了提升代碼的健壯性,在調用Setxx()時,提前用CanSet判斷,以免發生panic**
```
person := reflect.ValueOf(&Person{
Name: "叫我小叮當啊",
age: 18,
gender: 1,
})
//不可導出字段不能設置
age := person.Elem().FieldByName("age")
if age.CanSet(){
fmt.Println("can set")
}else{
fmt.Println("not can set") //進入這里
}
//可設置字段
name := person.Elem().FieldByName("Name")
if name.CanSet(){
name.SetString("啊哈哈")
fmt.Println(name) //啊哈哈
}else{
fmt.Println("not can set")
}
```
##### 修改slice
```
persons := make([]*Person,2,5)
persons[0] = &Person{
Name: "叫我小叮當啊",
age: 18,
gender: 1,
}
persons[1] = &Person{
Name: "大雄",
age: 28,
gender: 2,
}
p := reflect.ValueOf(&persons)
if p.Elem().Len() > 0 {
p.Elem().Index(0).Elem().FieldByName("Name").SetString("hahah")
fmt.Println(persons[0].Name)//hahah
}
//修改cap 初始是2
//設置的cap大小必須原始的len到cap之間,即只能把cap改小
p.Elem().SetCap(3)
fmt.Println(cap(persons))// 3
//修改len,設置時注意cap的大小
p.Elem().SetLen(3)
p.Elem().Index(2).Set(reflect.ValueOf(&Person{
Name: "靜香",
age: 28,
gender: 2,
}))
fmt.Println(persons[2].Name) //靜香
```
##### 修改map
```
persons := make(map[int]*Person,5)
persons[0] = &Person{
Name: "叫我小叮當啊",
age: 18,
gender: 1,
}
p1 := &Person{
Name: "大雄",
age: 28,
gender: 2,
}
pV := reflect.ValueOf(&persons)
pV.Elem().SetMapIndex(reflect.ValueOf(1),reflect.ValueOf(p1))
fmt.Println(persons[1])//&{大雄 28 2}
//修改值
pV.Elem().MapIndex(reflect.ValueOf(1)).Elem().FieldByName("Name").SetString("胖虎")
fmt.Println(persons[1])//&{胖虎 28 2}
```
#### 創建對象
##### 創建slice
```
var users []Person
usersType := reflect.TypeOf(users)
usersValue := reflect.MakeSlice(usersType, 1,2)
p1 := Person{
Name: "大雄",
age: 28,
gender: 2,
}
usersValue.Index(0).Set(reflect.ValueOf(p1))
user := usersValue.Interface().([]Person)
fmt.Println(user)//[{大雄 28 2}]
```
##### 創建map
```
var users map[int]*Person
usersType := reflect.TypeOf(users)
//usersValue := reflect.MakeMap(usersType)//這個可以創建
usersValue := reflect.MakeMapWithSize(usersType,3)//這個也可以
user := &Person{
Name: "大雄",
age: 28,
gender: 2,
}
usersValue.SetMapIndex(reflect.ValueOf(1),reflect.ValueOf(user))
usersValue.MapIndex(reflect.ValueOf(1)).Elem().FieldByName("Name").SetString("小叮當")
values := usersValue.Interface().(map[int]*Person)
fmt.Println(values[1])//&{小叮當 28 2}
```
##### 創建struct
```
tmp := reflect.TypeOf(Person{})
value := reflect.New(tmp)//new 轉化到指針 value
value.Elem().FieldByName("Name").SetString("小夫")
fmt.Println(value)//&{小夫 0 0}
```
- Go準備工作
- 依賴管理
- Go基礎
- 1、變量和常量
- 2、基本數據類型
- 3、運算符
- 4、流程控制
- 5、數組
- 數組聲明和初始化
- 遍歷
- 數組是值類型
- 6、切片
- 定義
- slice其他內容
- 7、map
- 8、函數
- 函數基礎
- 函數進階
- 9、指針
- 10、結構體
- 類型別名和自定義類型
- 結構體
- 11、接口
- 12、反射
- 13、并發
- 14、網絡編程
- 15、單元測試
- Go常用庫/包
- Context
- time
- strings/strconv
- file
- http
- Go常用第三方包
- Go優化
- Go問題排查
- Go框架
- 基礎知識點的思考
- 面試題
- 八股文
- 操作系統
- 整理一份資料
- interface
- array
- slice
- map
- MUTEX
- RWMUTEX
- Channel
- waitGroup
- context
- reflect
- gc
- GMP和CSP
- Select
- Docker
- 基本命令
- dockerfile
- docker-compose
- rpc和grpc
- consul和etcd
- ETCD
- consul
- gin
- 一些小點
- 樹
- K8s
- ES
- pprof
- mycat
- nginx
- 整理后的面試題
- 基礎
- Map
- Chan
- GC
- GMP
- 并發
- 內存
- 算法
- docker