Field field = clazz.getField("hello");
field.get(obj1);
field.get(obj2);
这个取得的反射对象类型是 java.lang.reflect.Field。它是可以复用的。只要传入不同的obj,就可以取得这个obj上对应的 field。但是 golang 的反射不是这样设计的
type_ := reflect.TypeOf(obj)
field, _ := type_.FieldByName("hello")
这里取出来的 field 对象是 reflect.StructField 类型,但是它没有办法用来取得对应对象上的值。如果要取值,得用另外一套对object,而不是type的反射
type_ := reflect.ValueOf(obj)
fieldValue := type_.FieldByName("hello")
这里取出来的 fieldValue 类型是 reflect.Value,它是一个具体的值,而不是一个可复用的反射对象了。
这就很蛋疼了!每次反射都需要malloc这个reflect.Value结构体。golang的反射性能怎么可能快?
Jsoniter 是 golang 实现的,基于反射的 JSON 解析器。其实现原理是用 reflect.Type 得出来的信息来直接做反射,而不依赖于 reflect.ValueOf。具体是怎么实现的呢?
结构体
先解决一个小问题。怎么利用 reflect.StructField 取得对象上的值?
对应的代码在: go/feature_reflect_object.go at master · json-iterator/go · GitHub
fieldPtr := uintptr(structPtr) + field.Offset
在 reflect.StructField 上有一个 Offset 的属性。利用这个可以计算出字段的指针值。我们可以写一个小测试来验证,这个是对的。
type TestObj struct {
field1 string
}
struct_ := &TestObj{}
field, _ := reflect.TypeOf(struct_).Elem().FieldByName("field1")
field1Ptr := uintptr(unsafe.Pointer(struct_)) + field.Offset
*((*string)(unsafe.Pointer(field1Ptr))) = "hello"
fmt.Println(struct_)
打印出来的消息是 &{hello}
获取 interface{} 的指针
如果对应的结构体是以 interface{} 传进来的。还需要从 interface{} 上取得结构体的指针
type TestObj struct {
field1 string
}
struct_ := &TestObj{}
structInter := (interface{})(struct_)
// emptyInterface is the header for an interface{} value.
type emptyInterface struct {
typ *struct{}
word unsafe.Pointer
}
structPtr := (*emptyInterface)(unsafe.Pointer(&structInter)).word
field, _ := reflect.TypeOf(structInter).Elem().FieldByName("field1")
field1Ptr := uintptr(structPtr) + field.Offset
*((*string)(unsafe.Pointer(field1Ptr))) = "hello"
fmt.Println(struct_)
Slice
搞定了结构体,接下来就是处理slice类型了。
对应的代码在:go/feature_reflect_array.go at master · json-iterator/go · GitHub
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
slice 的秘诀在于取出指向数组头部的指针,然后具体的元素,通过偏移量来计算。
slice := []string{"hello", "world"}
type sliceHeader struct {
Data unsafe.Pointer
Len int
Cap int
}
header := (*sliceHeader)(unsafe.Pointer(&slice))
fmt.Println(header.Len)
elementType := reflect.TypeOf(slice).Elem()
secondElementPtr := uintptr(header.Data) + elementType.Size()
*((*string)(unsafe.Pointer(secondElementPtr))) = "!!!"
fmt.Println(slice)
打印出来的内容
2
[hello !!!]
Map
对于 Map 类型来说,没有 reflect.ValueOf 之外的获取其内容的方式。所以还是只能老老实实地用golang自带的值反射api。
有疑问加站长微信联系(非本文作者)