反射機制是現代編程語言中一個比較高級的特性
在編譯時不知道類型的情況下,通過反射機制可以獲取對象的類型、值、方法甚至動態改變對象的成員,這就是反射機制
反射的意思是在運行時,能夠動態知道給定數據對象的類型和結構,並有機會修改它!
現在一個數據對象,如何判斷它是什么結構?數據interface中保存有結構數據呀,只要想辦法拿到該數據對應的內存地址,然后把該數據轉成interface,通過查看interface中的類型結構,就可以知道該數據的結構了呀~ 其實以上就是Go反射通俗的原理。
Go反射簡介
Go反射有三大法則:
1. Reflection goes from interface value to reflection object.
接口數據 -> 反射對象
2. Reflection goes from reflection object to interface value.
反射對象 ===> 接口數據
... v := reflect.ValueOf(x) o := v.Interface().(float64) ...
3. To modify a reflection object, the value must be settable.
倘若數據可更改,可通過反射對象來修改它
Go 的反射就是對以上三項法則的實現。
Go的反射主要由兩部分組成:Type和 Value, Type和 Value是倆結構體。
你會發現反射的實現和interface的組成很相似,都是由“類型”和“數據值”構成,但是值得注意的是:interface的“類型”和“數據值”是在“一起的”,而反射的“類型”和“數據值”是分開的。
Type和 Value提供了非常多的方法:例如獲取對象的屬性列表、獲取和修改某個屬性的值、對象所屬結構體的名字、對象的底層類型(underlying type)等等
Go中的反射,在使用中最核心的就兩個函數:
- reflect.TypeOf(x)
- reflect.ValueOf(x)
這兩個函數可以分別將給定的數據對象轉化為以上的 Type和 Value。這兩個都叫做 反射對象
如何通過反射對象來修改原數據對象的值呢?
在目前以上的所有例子中,反射得到的 Value對象的修改,都是無法直接修改原數據對象的。
如何才能可以通過反射對象來修改原數據對象的值或者說為什么不能設置呢?
原因簡單且純粹:在Go中,任何函數的參數都是值的拷貝,而非原數據。
反射函數 reflect.ValueOf()也不例外。我們目前得到的反射對象,都是原對象的copy的反射對象,而非原對象本身,所以不可以修改到原對象;即使可以修改,修改一個傳參時候的副本,也毫無意義,不如報錯兒。Go反射第三法則中的制定的 settable屬性就由此而來,還延伸出了類似於 CanSet()的方法。
那如何修改呢?
首先,在Go中要想讓函數“有副作用“,傳值必須傳指針類型的。
... var x float64 = 3.4 v := reflect.ValueOf(&x) ...
此時還不行,因為這樣反射對象對應的是原數據對象的指針類型,必須要拿到當前類型的值類型(*v),如何做?Go提供了另外一個方法 Elem()
... var x float64 = 3.4 v := reflect.ValueOf(&x) p := v.Elem() fmt.Println(p.CanSet()) // true p.SetFloat(7.1) fmt.Println(x) // 7.1
看以上代碼,就可以修改原數據了。