反射
計算機中提到的反射一般是指振坚,程序借助某種手段檢查自己結(jié)構(gòu)的一種能力后众,通常就是借助編程語言中定義的類型(types)窑邦。因此垢油,反射是建立在類型系統(tǒng)上的。
go是靜態(tài)類型化琅催,每個變量都有一個靜態(tài)類型居凶,也就是說,在編譯時藤抡,變量的類型就已經(jīng)確定侠碧。不顯示地去做強制類型轉(zhuǎn)換,不同類型之間是無法相互賦值的缠黍。
有一種特殊的類型叫做接口(interface type)弄兜,一個接口表示的是一組方法集合。一個接口變量能存儲任何具體的值瓷式,只要這個值實現(xiàn)了這個接口的方法集合替饿。比如io包中的Reader和Writer,io.Reader接口變量能夠保存任意實現(xiàn)了Read方法的類型所定義的值贸典。
一個特殊接口就是空接口interface{}盛垦,任何值都可以說實現(xiàn)了空接口,因為空接口中沒有定義方法瓤漏,所以空接口可以保存任何值腾夯。
一個接口類型變量存儲了一對值:賦值給這個接口變量的具體值 + 這個值的類型描述符颊埃。更進一步的講,這個“值”是實現(xiàn)了這個接口的底層具體數(shù)據(jù)項(underlying concrete data item)蝶俱,而這個“類型”是描述了具體數(shù)據(jù)項(item)的全類型(full type)班利。
所以反射是干嘛的呢?反射是一種檢查存儲在接口變量中的(類型榨呆,值)對的機制罗标。reflect包中提供的2個類型Type和Value,提供了訪問接口值的reflect.Type和reflect.Value部分积蜻。
三大法則
- Reflection goes from interface value to reflecton object:從interface{} 變量可以反射出反射對象闯割;
type MyInt int32
func main() {
var x MyInt = 7
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
fmt.Println("type:", t) // type: main.MyInt
fmt.Println("value:", v) // value: 7
fmt.Println("kind:", v.Kind()) // kind: int32
fmt.Println("type:", v.Type()) // type: main.MyInt
x = MyInt(int32(v.Int)) // v.Int returns a int64
}
? reflect.Value的Type返回的是靜態(tài)類型MyInt,而kind()方法返回的是底層類型int32竿拆;為了保持API簡單宙拉,value的Setter和Getter類型方法操作,是包含某個值的最大類型丙笋,v.Int()返回的是int64谢澈,必要時轉(zhuǎn)化成實際類型。
- Reflection goes from reflection object to interface value:從反射對象可以獲取 interface{} 變量御板;
type MyInt int32
func main() {
var x MyInt = 7
v := reflect.ValueOf(x)
y := v.Interface().(int32)
fmt.Println(y) // 7
}
對于一個reflect.Value锥忿,可以用Interface方法恢復成一個接口值,效果就是包類型和值打包成接口怠肋,并返回結(jié)果敬鬓。
- To modify a reflection object, the value must be settable:要修改反射對象,其值必須可設置笙各;
func main() {
var x float64 = 3.4
v := reflect.ValueOf(x)
// panic: reflect: reflect.flag.mustBeAssignable using addressable value
v.SetFloat(7.1)
p := reflect.ValueOf(&X)
// panic: reflect: reflect.flag.mustBeAssignable using addressable value
p.SetFloat(7.1)
e := reflect.ValueOf(&X).Elem()
// OK
e.SetFloat(7.1)
}
如果我們想通過反射來修改x列林,我們必須把我們想要修改的值的指針傳給一個反射庫。Go 語言的函數(shù)調(diào)用都是值傳遞的酪惭,所以我們只能先獲取指針對應的 reflect.Value希痴,再通過 reflect.Value.Elem 方法迂回的方式得到可以被設置的變量。我們通過如下所示的代碼理解這個過程:
func main() {
i := 1
v := &i
*v = 10
}
如果不能直接操作 i 變量修改其持有的值春感,我們就只能獲取 i 變量所在地址并使用 *v 修改所在地址中存儲的整數(shù)砌创。
相關(guān)資料:
[1] Go語言中反射包的實現(xiàn)原理 https://studygolang.com/articles/2157
[2] 4.3反射 https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/