反射的概念
反射就是程序能夠在運行時動態(tài)的查看自己的狀態(tài)流译,比關(guān)切允許修改自
身的行為。
1肤无、GO的反射基礎(chǔ)是接口和類型系統(tǒng)先蒋,
2、反射的優(yōu)點:1)宛渐、通用性,可對類庫和框架代碼做極大的簡化設(shè)計眯搭;2)窥翩、靈活性,對一些測試工具的開發(fā)提供良好的支持鳞仙。
3寇蚊、反射的缺點:1)、脆弱棍好,不正確的修改很容易導(dǎo)致程序的崩潰仗岸;2)、難懂借笙,反射接口運行時扒怖,沒有具體的類型系統(tǒng)的約束,接口的抽象及實現(xiàn)細(xì)節(jié)復(fù)雜业稼,導(dǎo)致代碼難以理解盗痒;3)、性能損失低散,動態(tài)修改狀態(tài)必然不是直接的地址引用俯邓,而是借助運行時構(gòu)造一個抽象層來間接訪問,導(dǎo)致性能損失熔号。
4稽鞭、用途:盡量在一些庫或框架內(nèi)部代碼中使用。
5引镊、Golang語言實現(xiàn)了反射朦蕴,反射機(jī)制就是在運行時動態(tài)的調(diào)用對象的方法和屬性篮条,Golang的gRPC也是通過反射實現(xiàn)的。
所有類型通用的方法
//返回包含包名的類型名字梦重,對于未命名類型返回的是空
Name() string
//Kind返回該類型的底層基礎(chǔ)類型
Kind() Kind
//確定當(dāng)前類型是否實現(xiàn)了u接口類型
//注意這里的u必須是接口類型的Type
Implements(u Type) bool
//判斷當(dāng)前類型的實例是否能賦值給type為u的類型變量
AssignableTo(u Type) bool
//判斷當(dāng)前類型的實例是否能強(qiáng)制類型轉(zhuǎn)換為u類型變量
ConvertibleTo(u Type) bool
//判斷當(dāng)前類型是否支持比較(等于或不等于)
//支持等于的類型可以作為map的key
Comparable() bool
//返回一個類型的方法的個數(shù)
NumMethod() int
//通過索引值訪問方法兑燥,索引值必須屬于[0,NumMethod()]琴拧,否則引發(fā)panic
Method(int) Method
//通過方法名獲取Method
MethodByName(string) (Method,bool)
//返回類型的包路徑降瞳,如果類型是預(yù)聲明類型或未命名類型,則返回空字符串
PkgPath() string
//返回存放該類型的實例需要多大的字節(jié)空間
size() uintptr
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) ReflectCallFunc() {
fmt.Println("Allen.Wu ReflectCallFunc")
}
func main() {
user := User{1, "Allen.Wu", 25}
DoFiledAndMethod(user)
}
// 通過接口來獲取任意參數(shù)蚓胸,然后一一揭曉
func DoFiledAndMethod(input interface{}) {
getType := reflect.TypeOf(input)
fmt.Println("get Type is :", getType.Name())
getValue := reflect.ValueOf(input)
fmt.Println("get all Fields is:", getValue)
// 獲取方法字段
// 1. 先獲取interface的reflect.Type挣饥,然后通過NumField進(jìn)行遍歷
// 2. 再通過reflect.Type的Field獲取其Field
// 3. 最后通過Field的Interface()得到對應(yīng)的value
for i := 0; i < getType.NumField(); i++ {
field := getType.Field(i)
value := getValue.Field(i).Interface()
fmt.Printf("%s: %v = %v\n", field.Name, field.Type, value)
}
// 獲取方法
// 1. 先獲取interface的reflect.Type,然后通過.NumMethod進(jìn)行遍歷
for i := 0; i < getType.NumMethod(); i++ {
m := getType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}
運行結(jié)果:
get Type is : User
get all Fields is: {1 Allen.Wu 25}
Id: int = 1
Name: string = Allen.Wu
Age: int = 25
ReflectCallFunc: func(main.User)
package main
import (
"fmt"
"reflect"
)
type User struct {
Id int
Name string
Age int
}
func (u User) ReflectCallFuncHasArgs(name string, age int) {
fmt.Println("ReflectCallFuncHasArgs name: ", name, ", age:", age, "and origal User.Name:", u.Name)
}
func (u User) ReflectCallFuncNoArgs() {
fmt.Println("ReflectCallFuncNoArgs")
}
// 如何通過反射來進(jìn)行方法的調(diào)用沛膳?
// 本來可以用u.ReflectCallFuncXXX直接調(diào)用的扔枫,但是如果要通過反射,那么首先要將方法注冊锹安,也就是MethodByName短荐,然后通過反射調(diào)動mv.Call
func main() {
user := User{1, "Allen.Wu", 25}
// 1. 要通過反射來調(diào)用起對應(yīng)的方法,必須要先通過reflect.ValueOf(interface)來獲取到reflect.Value叹哭,得到“反射類型對象”后才能做下一步處理
getValue := reflect.ValueOf(user)
// 一定要指定參數(shù)為正確的方法名
// 2. 先看看帶有參數(shù)的調(diào)用方法
methodValue := getValue.MethodByName("ReflectCallFuncHasArgs")
args := []reflect.Value{reflect.ValueOf("wudebao"), reflect.ValueOf(30)}
methodValue.Call(args)
// 一定要指定參數(shù)為正確的方法名
// 3. 再看看無參數(shù)的調(diào)用方法
methodValue = getValue.MethodByName("ReflectCallFuncNoArgs")
args = make([]reflect.Value, 0)
methodValue.Call(args)
}
運行結(jié)果:
ReflectCallFuncHasArgs name: wudebao , age: 30 and origal User.Name: Allen.Wu
ReflectCallFuncNoArgs
說明
1忍宋、要通過反射來調(diào)用起對應(yīng)的方法,必須要先通過reflect.ValueOf(interface)來獲取到reflect.Value风罩,得到“反射類型對象”后才能做下一步處理
2糠排、reflect.Value.MethodByName這.MethodByName,需要指定準(zhǔn)確真實的方法名字超升,如果錯誤將直接panic入宦,MethodByName返回一個函數(shù)值對應(yīng)的reflect.Value方法的名字。
3室琢、[]reflect.Value乾闰,這個是最終需要調(diào)用的方法的參數(shù),可以沒有或者一個或者多個研乒,根據(jù)實際參數(shù)來定汹忠。
4、reflect.Value的 Call 這個方法雹熬,這個方法將最終調(diào)用真實的方法宽菜,參數(shù)務(wù)必保持一致,如果reflect.Value'Kind不是一個方法竿报,那么將直接panic铅乡。
5、本來可以用u.ReflectCallFuncXXX直接調(diào)用的烈菌,但是如果要通過反射阵幸,那么首先要將方法注冊花履,也就是MethodByName,然后通過反射調(diào)用methodValue.Call
使用反射的實例:inject包 依賴注入:https://www.zddhub.com/memo/2015/07/05/go-dependency-inject.html
反射三定律:
(1)反射可以從接口值得到反射對象挚赊;
(2)反射可以從反射對象獲取接口值诡壁;
(3)若要修改一個反射對象,則其值必須為可修改荠割。
1妹卿、Golang的gRPC也是通過反射實現(xiàn)的。
2蔑鹦、控制反轉(zhuǎn)夺克,依賴注入
類型斷言在GO中指的就是判斷接口型變量運行時的實際類型