反射是程序校验本身数据结构和类型的一种机制。文章尝试解释Golang的反射机制工做原理,每种编程语言的反射模型都是不一样的,有不少语言甚至都不支持反射。golang
在将反射以前须要先介绍下接口interface,由于Golang的反射实现是基于interface的。Golang是静态类型语言,每一个变量拥有一个静态类型,在编译器就已经肯定,例如int,float32,*MyType, []byte等等。若是咱们定义:编程
type MyInt int var i int var j MyInt
int类型的I和MyInt类型的j是不一样类型的变量,在没有限制类型转换的状况下它们不能相互赋值,即使它们的底层类型是同样的。json
接口interface类型是最重要的一种数据类型,表明的一些方法的集合。interface变量能够存储任意的数据类型,只要该数据类型实现了interface的方法集合。例如io包的io.Reader和io.Writer:数组
// Reader is the interface that wraps the basic Read method. type Reader interface { Read(p []byte) (n int, err error) } // Writer is the interface that wraps the basic Write method. type Writer interface { Write(p []byte) (n int, err error) }
任意实现了Read方法的类型都是Reader类型,也就是说能够赋值给Reader接口,换句话说就是Reader interface能够存储任意的实现了Read方法的类型:数据结构
var r io.Reader r = os.Stdin r = bufio.NewReader(r) r = new(bytes.Buffer) // and so on
须要明确的是不管上述变量r实际存储的是什么类型,r的类型永远都是io.Reader,这就是为何说Golang是静态类型编程语言,由于r声明时是io.Reader,在编译期就已经明确了类型。
编程语言
Interface一个特别重要的示例是空接口:函数
interface{}
它表明一个空的方法集合,由于任意类型值都有0个多少多个方法,因此空的接口interface{}能够存储任意类型值。post
有些人说Golang的interface是动态类型,实际上是种误解。接口是静态类型,interface变量定义时就声明了一种静态类型,即使interface存储的值在运行时会改变类型,可是interface的类型是必定的。编码
一个interface类型变量会存储一对数据,具体类型的值和值的具体类型(value, concrete type)。例如:spa
var r io.Reader tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0) if err != nil { return nil, err } r = tty
上述的interface变量I会存储一对数据(tty,*os.File)。须要注意的是*os.File类型不止单单实现了Read方法,还实现了其余方法,好比Write方法。即使interface类型变量i值提供了访问Read的方法,i仍是携带了*os.File变量的全部类型信息。因此能够将i转换为io.Writer类型:
var w io.Writer w = r.(io.Writer)
上述的表达式是一个类型断言,断言r也实现了io.Writer,因此能够赋值给w,不然会panic。完成赋值后,w会携带一对值(tty,*os.File),和r同样的一对值。接口的静态类型决定了上述的tty可以调用的方法,即使它实际上包含了更多的方法。
也能够将它赋值给空接口:
var empty interface{} empty = w
空接口empty也携带一样的对值(tty,*os.File)。由于任意的类型都是空接口因此不用转换。
从本质上讲,反射是校验接口存储(value,concrete type)值对的一种机制。分别对应的reflect包的Value和Type类型。经过Value和Type类型能够访问到interface变量的储存内容,reflect.TypeOf和reflect.ValueOf将会返回interface变量的reflect.Type和reflect.Value类型值。
从TypeOf开始:
package main import ( "fmt" "reflect" ) func main() { var x float64 = 3.4 fmt.Println("type:", reflect.TypeOf(x)) }
结果将会输出:
type: float64
你可能会有疑问,反射是基于interface,那么这里的interface在哪儿呢?这就须要了解TypeOf的定义:
// TypeOf returns the reflection Type of the value in the interface{}. func TypeOf(i interface{}) Type
也就是说TypeOf会用interface{}把参数储存起来,而后reflect.TypeOf再从interface{}中获取信息。
同理ValueOf的函数定义为:
// ValueOf returns a new Value initialized to the concrete value // stored in the interface i. ValueOf(nil) returns the zero Value. func ValueOf(i interface{}) Value
示例:
var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("type:", v.Type()) fmt.Println("kind is float64:", v.Kind() == reflect.Float64) fmt.Println("value:", v.Float())
结果输出:
type: float64 kind is float64: true value: 3.4
因此咱们能够得出反射的第一条规则是:反射对象是从接口值获取的。
规则2:能够从反射对象中获取接口值。
利用reflect.Value的Interface方法能够得到传递过来的空接口interface{}:
// Interface returns v's value as an interface{}. func (v Value) Interface() interface{}
示例:
y := v.Interface().(float64) // y will have type float64. fmt.Println(y)
规则3:经过反射对象的set方法能够修改实际储存的变量,前提是存储的变量是能够被修改的。
反射定义变量是能够被修改的(settable)条件是传递变量的指针,由于若是是值传递的话,反射对象set方法改变的是一份拷贝,因此会显得怪异并且没有意义,因此干脆就将值传递的状况定义为不可修改的,若是尝试修改就会触发panic。
示例:
var x float64 = 3.4 v := reflect.ValueOf(x) v.SetFloat(7.1) // Error: will panic
报错以下:
panic: reflect.Value.SetFloat using unaddressable value
能够经过反射对象Value的CanSet方法判断是不是可修改的:
var x float64 = 3.4 v := reflect.ValueOf(x) fmt.Println("settability of v:", v.CanSet())
输出:
settability of v: false
可被修改的状况:
var x float64 = 3.4 p := reflect.ValueOf(&x) // Note: take the address of x. fmt.Println("type of p:", p.Type()) fmt.Println("settability of p:", p.CanSet())
输出:
type of p: *float64 settability of p: false
反射对象p是不可被修改的,由于p不是咱们想要修改的,*p才是。调用Value的Elem方法能够获取p指向的内容,而且内容储存在Value对象中:
v := p.Elem() fmt.Println("settability of v:", v.CanSet())
输出:
settability of v: true
示例:
v.SetFloat(7.1) fmt.Println(v.Interface()) fmt.Println(x)
输出:
7.1 7.1
只要有结构体的地址咱们就能够用反射修改结构体的内容。下面是个简单的示例:
type T struct { A int B string } t := T{23, "skidoo"} s := reflect.ValueOf(&t).Elem() typeOfT := s.Type() for i := 0; i < s.NumField(); i++ { f := s.Field(i) fmt.Printf("%d: %s %s = %v\n", i, typeOfT.Field(i).Name, f.Type(), f.Interface()) }
程序输出:
0: A int = 23 1: B string = skidoo
修改:
s.Field(0).SetInt(77) s.Field(1).SetString("Sunset Strip") fmt.Println("t is now", t)
程序输出:
t is now {77 Sunset Strip}
因此反射的三条规则总结以下:
规则1:反射对象是从接口值获取的。
规则2:能够从反射对象中获取接口值。
规则3:经过反射对象的set方法能够修改实际储存的settable变量
因为Json的序列化(编码)和反序列化(解码)都会用到反射,因此这里放在一块儿讲解。
能够用Marshal函数完成Json编码:
func Marshal(v interface{}) ([]byte, error)
给定一个Golang的结构体Message:
type Message struct { Name string Body string Time int64 }
Message的实例m为:
m := Message{"Alice", "Hello", 1294706395881547000}
Marshal编码Json:
b, err := json.Marshal(m)
若是工做正常,err为nil,b为[]byte类型的Json字符串:
b == []byte(`{"Name":"Alice","Body":"Hello","Time":1294706395881547000}`)
Json编码规则:
1.Json对象只支持string做为key;因此想要编码Golang map类型必须是map[stirng]T,其中T表示Golang支持的任意类型。
2.Channel,complex和函数类型不能被编码
3.循环引用嵌套的结构体不支持,他们会形成Marshal进入一个未知的循环体重
4.指针将会被编码指向的内容自己,若是指针是nil将会是null
能够用Unmarshal解码Json数据:
func Unmarshal(data []byte, v interface{}) error
首先咱们必需要先建立解码数据存储的变量:
var m Message
而后传递变量的指针(参考反射规则3):
err := json.Unmarshal(b, &m)
若是b包含可用的Json而且适合m,那么err将会是nil,b的数据会被存储在m中,就好像下面的赋值同样:
m = Message{ Name: "Alice", Body: "Hello", Time: 1294706395881547000, }
Unmarshal是怎么识别要存储的解码字段的呢?例如Json的一个Key为”Foo”,Unmarshal会找根据下面的规则顺序匹配:
1.找名为“Foo”的字段tag
2.找名为“Foo”,”FOO”或者“FoO”的字段名称
再看下面的Json数据解码会匹配到Golang的什么数据类型呢:
b := []byte(`{"Name":"Bob","Food":"Pickle"}`) var m Message err := json.Unmarshal(b, &m)
Unmarshal只会解码它认识的字段。在这个例子中,只有Name字段出如今m中,因此Food字段会被忽略。当你想在一个大的Json数据中提取你要想的部分字段时,该特性是很是有用的。这意味着你不须要关心Json的全部字段,只须要关心你要用到的字段便可。
json包会用map[string]interface{}存储Json对象,用[]interface{}存储数组。当Unmarshal Json对象做为interface{}值时,默认Golang的concrete type为:
Json booleans类型默认为bool
Json 数字默认为float64
Json strings默认为string
Json null默认为nil
示例:
b := []byte(`{"Name":"Wednesday","Age":6,"Parents":["Gomez","Morticia"]}`) var f interface{} err := json.Unmarshal(b, &f)
相对于下面的赋值操做:
f = map[string]interface{}{ "Name": "Wednesday", "Age": 6, "Parents": []interface{}{ "Gomez", "Morticia", }, }
若是想要访问f的底层map[string]interface{}数据结构须要断言:
m := f.(map[string]interface{})
而后遍历map接着访问其余成员:
for k, v := range m { switch vv := v.(type) { case string: fmt.Println(k, "is string", vv) case float64: fmt.Println(k, "is float64", vv) case []interface{}: fmt.Println(k, "is an array:") for i, u := range vv { fmt.Println(i, u) } default: fmt.Println(k, "is of a type I don't know how to handle") } }
上述示例中,能够定义一个结构体来存储:
type FamilyMember struct { Name string Age int Parents []string } var m FamilyMember err := json.Unmarshal(b, &m)
Unmarshal数据进入FamilyMembear值时,会自动给nil 切片分配内存,同理若是有指针,map也会自动分配内存。
文章介绍了interface、reflection、json,其中reflection是基于interface实现的,而json的编码和解码用到了reflection。
https://blog.golang.org/json-and-go