理解接口包含的(value, type)對很重要golang
Go的接口都是靜態類型化的:一個接口類型變量老是保持同一個靜態類型,即便在運行時它保存的值的類型發生變化,這些值老是知足這個接口。函數
一個接口存儲一個pair:賦值給這個接口變量的具體值,以及這個值的類型描述符.ui
var r io.Reader tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty //此時 r包含了(value, type)這對值, 即(tty, os.File)
雖然 io.Reader只提供了 Read 方法,可是咱們依然能夠得到到具體的 (tty, *os.File) 對, 因此任然可使用這個值得所有信息.this
好比咱們能夠將其斷言成 io.Writer指針
var w io.Writer w = r.(io.Writer) // w 包含的依然是 (tty, *os.File)
一樣能夠再賦值給空接口 interface{}code
var empty interface{} empty = w //empty 依然是 (tty, *os.File)
因此咱們老是能夠得到這個具體的值,這樣就爲反射作好準備.對象
使用反射,首先要知道這兩個的區別blog
var str string = "this is a string" Type => string Value => this is a string 源碼能夠看到: //Type 是個接口 type Type interface{ //一些方法 } //Value 是個 struct type Value struct { //屬性 } //源碼 //rtype 實現了 Type 接口 type rtype struct { //實際使用的時候用的是這個 }
** TypeOf 和 ValueOf 是獲取 Type 和 Value 的方法**接口
// TypeOf returns the reflection Type of the value in the interface{}. func TypeOf(i interface{}) Type
當咱們調用reflect.Typeof(x)的時候,x首先被保存到一個空接口中,這個空接口而後被做爲參數傳遞。reflect.Typeof 會把這個空接口拆包(unpack)恢復出類型信息。ip
例如
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x)) fmt.Println("value:", reflect.ValueOf(x)) //Valueof方法會返回一個Value類型的對象 }
type: float64 value: 3.4 //文檔 上寫的是這樣的, 也就是表示值得意思 value: <float64 Value>
從Value到達Type是經過Value中定義的某些方法來實現的
Value 類型有 Type() 方法能夠返回這個value的 Type的類型
(這個方法返回的是值的靜態類型即static type,也就是說若是定義了type MyInt int64,那麼這個函數返回的是MyInt類型而不是int64,看後面那個Kind方法就能夠理解了) Type和Value都有一個Kind方法能夠返回一個常量用於指示一個項究竟是以什麼形式(也就是底層類型即underlying type,繼續前面括號裏提到的,Kind返回的是int64而不是MyInt)
package main import ( "fmt" "reflect" ) type MyInt int64 func main() { var x MyInt = 3 fmt.Println("type:", reflect.TypeOf(x)) fmt.Println("value:", reflect.ValueOf(x)) //Valueof方法會返回一個Value類型的對象 fmt.Println(reflect.ValueOf(x).Type()) fmt.Println(reflect.TypeOf(x).Kind()) fmt.Println(reflect.ValueOf(x).Kind()) } //結果 type: main.MyInt value: 3 main.MyInt int64 int64
Reflection goes from interface value to reflection object.
keep the API simple
官方:第一個性質是,爲了保持API簡單,Value的」setter」和「getter」類型的方法操做的是能夠包含某個值的最大類型
其實說的就是 如 reflect包中 Int(), Uint(), Float()等方法 返回的都是最大的那個如int64, int64, float64等. SetInt(int64), SetFloat(float64)等.
package main import ( "fmt" "reflect" ) func main() { var x uint8 = 'x' v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) // uint8. fmt.Println("kind is uint8: ", v.Kind() == reflect.Uint8) // true. x = uint8(v.Uint())// v.Uint returns a uint64.看到啦嘛?這個地方必須進行強制類型轉換! fmt.Println(reflect.TypeOf(v.Uint())) // } //結果 type: uint8 kind is uint8: true uint64
官方:第二個性質是,反射對象(reflection object)的Kind描述的是底層類型(underlying type),而不是靜態類型(static type)
type MyInt int var x MyInt = 7 v := reflect.ValueOf(x) // v.Kind() 爲 reflect.Int 即便x的靜態類型是MyInt而不是int。換句話說,Kind不能將一個int從一個MyInt中區別出來,可是Type能作到 使用value.Type() 結果是 MyInt
Reflection goes from reflection object to interface value
也就是說反射對象能夠逆反射成接口
給定一個reflect.Value,咱們能用Interface方法把它恢復成一個接口值;效果上就是這個Interface方法把類型和值的信息打包成一個接口表示而且返回結果
簡單說:Interface方法是Valueof函數的逆
// Interface returns v's value as an interface{}. func (v Value) Interface() interface{}
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.2 v := reflect.ValueOf(x) y := v.Interface().(float64) // y will have type float64. fmt.Println(y) //3.2 //fmt.Println能夠處理interface{}, 因此能夠直接 fmt.Println(v.Interface()) // 3.2 //由於是float, 因此也能夠用printf,不是不行 fmt.Printf("value is %7.1e\n", v.Interface()) //value is 3.2e+00 }
To modify a reflection object, the value must be settable.
修改反射對象,值必須是可settable的
這段代碼會發生panic
var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) // Error: will panic. //panic: reflect.Value.SetFloat using unaddressable value
問題不是不能尋址, 而是出在v不是settable的.
Settability是Value的一條性質,並且不是全部的Value都具有這條性質.
var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("settability of v:", v.CanSet()) //settability of v: false
也就是咱們 reflect.ValueOf創造出的接口值v,只是x的副本,因此不能賦值.
就像咱們使用函數f(x), 傳入的x是個副本,咱們不能用傳入的x修改原來的值.
除非咱們傳入的是f(&x)才能夠修改值,一樣反射若是想修改值,就要將要修改值得指針傳入反射庫.
var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x.注意這裏哦!咱們把x地址傳進去了! fmt.Println("type of p:", p.Type()) fmt.Println("settability of p:", p.CanSet()) //結果 type of p: *float64 settability of p: false
咱們傳入了x的指針,咱們想設置的是*p,爲了獲得他咱們使用 value.Elem()來得到具體的
v := p.Elem() fmt.Println("settability of v:", v.CanSet()) //settability of v: true v.SetFloat(7.1) fmt.Println(v.Interface()) //7.1 fmt.Println(x) // 7.1
reflection Values need the address of something in order to modify what they represent 反射Values爲了修改它們所表示的東西必需要有這些東西的地址
使用struct 進行反射,最大的問題就是在實例化的時候, 有多是直接賦值struct,而有一些是new出來的 表示的是指針, 這樣在反射方法的時候就會有一些問題.
type T struct { A int B string } t := T{23, "skidoo"} s := reflect.ValueOf(&t).Elem() //這裏傳入的是&t,因此是可設置的, 若是是t,就不能夠設置了 typeOfT := s.Type()//把s.Type()返回的Type對象複製給typeofT,typeofT也是一個反射。 for i := 0; i < s.NumField(); i++ { f := s.Field(i)//迭代s的各個域,注意每一個域仍然是反射。 fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface())//提取了每一個域的名字 } //結果 0: A int = 23 1: B string = skidoo
** 這裏T中首字母都是大寫的(可導出的)**,只有可導出的域纔是settable的.
由於傳入的是指針,因此是可修改的,那麼咱們能夠修改這些值.
s.Field(0).SetInt(77) s.Field(1).SetString("Sunset Strip") fmt.Println("t is now", t) t is now {77 Sunset Strip}
或者這樣也是同樣的
t := new(T) t.A = 23 t.B = "skidoo" s := reflect.ValueOf(t).Elem() //由於new完表示的就是指針