Toggle navigation
首页
技术
骑行
羽毛球
资讯
联络我
登录
golang的reflect和interface详解
2020-01-17
golang
> 本文通过实际示例来介绍interface{}空接口和reflect反射 ## interface{}空接口 inteface{}变量保存2个指针,一个指向具体的值,一个指向值的类型,在64位系统中,一个指针变量为64 bit, 8 byte,下面的测试用以验证: ```go t.Run("test size of interface{}", func(t *testing.T) { var i interface{} assert.Equal(t, uintptr(16), unsafe.Sizeof(i)) }) ``` 把值类型变量赋值给interface{}变量时,interface{}变量的值指针指向了一个值类型变量的副本,并不是值本身: ```go t.Run("test assign basic type to interface{}", func(t *testing.T) { var i interface{} = a assert.Equal(t, a, i.(int)) i = 2 assert.Equal(t, 1, a) }) ``` ## reflect.Value和reflect.Type reflect.ValueOf用于获取保存在interface{}中的值,但它返回的是reflect.Value类型,不是interface{}中的值类型,如果想要获取该值,可以通过 Interface 方法。reflect.ValueOf(i interface{})和reflect.Value.Interface()互为逆方法 ```go t.Run("test get value of interface{}", func(t *testing.T) { var i interface{} = 1 assert.NotEqual(t, 1, reflect.ValueOf(i)) assert.Equal(t, 1, reflect.ValueOf(i).Interface().(int)) }) ``` 可以通过 Set 方法来设置保存在 reflect.Value 中的值,但必须是可设置的才可以,否则会 pinic ```go t.Run("test set value of interface{}", func(t *testing.T) { a := 1 var i interface{} = a value := reflect.ValueOf(i) value.Set(reflect.ValueOf(2)) assert.Equal(t, 2, value.Interface().(int)) }) ``` 因为空接口 i 中保存的是变量 a 的值拷贝,所以即使可以修改 i 中保存的值,也无法改变 a 的值,golang 为了避免这种无效改变值,直接 panic。 可以通过 CanSet 方法来判断 reflect.Value 中保存的值是否可以做有效改变,以及通过引用传递来通过指针来改变原有变量的值 ```go t.Run("test set value of interface{}", func(t *testing.T) { a := 1 var i interface{} = a value := reflect.ValueOf(i) assert.Equal(t, false, value.CanSet()) i = &a value = reflect.ValueOf(i).Elem() assert.Equal(t, true, value.CanSet()) value.Set(reflect.ValueOf(2)) assert.Equal(t, 2, a) }) ``` i 现在保存的是变量 a 的指针地址,reflect.ValueOf(i).Elem() 返回变量 a 的指针,可以通过其来设置原有变量的值。另外注意 Set 方法传递的是 reflect.Value 类型 对于 struct 类型,也可以通过同样的方法来修改原有变量的值 ```go type Student struct { Age int Name string } t.Run("test set value of struct stored in interface{}", func(t *testing.T) { var i interface{} s := Student{Name: "Jason"} assert.Equal(t, "Jason", s.Name) i = &s value := reflect.ValueOf(i).Elem() value.Field(1).Set(reflect.ValueOf("Vincent")) assert.Equal(t, "Vincent", s.Name) }) ``` 对于slice类型,保存的是数组的指针,所以在赋值给 interface{} 的时候无需取地址即可更改原有数组 ```go t.Run("test set value of slice stored in interface{}", func(t *testing.T) { var i interface{} slice := []int{1, 2, 3} i = slice value := reflect.ValueOf(i) value.Interface().([]int)[0] = 4 assert.Equal(t, 4, slice[0]) }) ``` 那么,如果想要给 slice append 新的内容呢?实际上,即使传递 slice 变量的引用给 reflect.Value,它也是不可赋值的,因为 slice 本身不保存数组值,它保存的另外一个数组的指针: ```go type slice struct { array unsafe.Pointer len int cap int } ``` 根据这个思路,想要给 slice append 新的内容,做法就是生成新的 slice,然后赋值给原有变量: ```go t.Run("test append value of slice stored in interface{}", func(t *testing.T) { var i interface{} slice := []int{1, 2, 3} i = &slice value := reflect.ValueOf(i) assert.Equal(t, false, value.CanSet()) value = reflect.AppendSlice(value.Elem(), reflect.ValueOf([]int{4, 5, 6})) slice, ok := value.Interface().([]int) assert.True(t, ok) assert.Equal(t, 6, len(slice)) }) ``` 希望对理解reflect和interface有帮助 ## 参考 * [interfaces](https://research.swtch.com/interfaces) * [laws-of-reflection](https://blog.golang.org/laws-of-reflection)
×
本文为博主原创,如需转载,请注明出处:
http://www.supperxin.com
返回博客列表