深入理解Golang反射

反射

計算機中提到的反射一般是指,程序借助某種手段檢查自己結構的一種能力,通常就是借助編程語言中定義的類型(types)。因此,反射是建立在類型系統上的。

go是靜態類型化,每個變量都有一個靜態類型,也就是說,在編譯時,變量的類型就已經確定。不顯示地去做強制類型轉換,不同類型之間是無法相互賦值的。

有一種特殊的類型叫做接口(interface type),一個接口表示的是一組方法集合。一個接口變量能存儲任何具體的值,只要這個值實現了這個接口的方法集合。比如io包中的Reader和Writer,io.Reader接口變量能夠保存任意實現了Read方法的類型所定義的值。

一個特殊接口就是空接口interface{},任何值都可以說實現了空接口,因為空接口中沒有定義方法,所以空接口可以保存任何值

一個接口類型變量存儲了一對值:賦值給這個接口變量的具體值 + 這個值的類型描述符。更進一步的講,這個“值”是實現了這個接口的底層具體數據項(underlying concrete data item),而這個“類型”是描述了具體數據項(item)的全類型(full type)。

所以反射是干嘛的呢?反射是一種檢查存儲在接口變量中的(類型,值)對的機制。reflect包中提供的2個類型Type和Value,提供了訪問接口值的reflect.Type和reflect.Value部分。

三大法則

  1. 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返回的是靜態類型MyInt,而kind()方法返回的是底層類型int32;為了保持API簡單,value的Setter和Getter類型方法操作,是包含某個值的最大類型,v.Int()返回的是int64,必要時轉化成實際類型。

  1. 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方法恢復成一個接口值,效果就是包類型和值打包成接口,并返回結果。

  1. 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 語言的函數調用都是值傳遞的,所以我們只能先獲取指針對應的 reflect.Value,再通過 reflect.Value.Elem 方法迂回的方式得到可以被設置的變量。我們通過如下所示的代碼理解這個過程:

func main() {
    i := 1
    v := &i
    *v = 10
}

如果不能直接操作 i 變量修改其持有的值,我們就只能獲取 i 變量所在地址并使用 *v 修改所在地址中存儲的整數。

相關資料:

[1] Go語言中反射包的實現原理 https://studygolang.com/articles/2157

[2] 4.3反射 https://draveness.me/golang/docs/part2-foundation/ch04-basic/golang-reflect/

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • [TOC] Golang的反射reflect深入理解和示例 【記錄于2018年2月】 編程語言中反射的概念 在計算...
    AllenWu閱讀 869評論 1 14
  • 編程語言中反射的概念 在計算機科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過采用某種機...
    豆瓣奶茶閱讀 12,707評論 0 31
  • 歡迎訪問博客原文:http://pengtuo.tech/golang/2019/09/23/golang-ref...
    PeTu閱讀 640評論 0 0
  • 序言 第一次接觸反射技術是在很多年前學習設計模式的時候,那時在優化Java版簡單工廠的實現,當讀取配置信息中的的類...
    _張曉龍_閱讀 4,675評論 2 21
  • 公司:叢迪服裝有限公司 【日精進打卡第784天】 【知~學習】 《六項精進》大綱0遍,共406遍; 《六項精進》通...
    阿詩瑪_6209閱讀 144評論 0 0