学习golang的反射

简介: golang的反射

0x01 golang的反射

  1. 反射三定律

1.1. 反射第一定律
反射可以将interface类型变量转换成反射对象

var x float64 = 3.4
t := reflect.TypeOf(x) // reflect.Type类型
fmt.Println("type:", t)

v := reflect.ValueOf(x) // reflect.Value类型
fmt.Println("value:", v)

// type: float64
// value: 3.4

注意:反射是针对interface类型变量的,其中 TypeOf() 和 ValueOf() 接受的参数都是 interface{} 类型的,也即x值是被转成了interface传入的。
1.2. 反射第二定律
反射可以将反射对象还原成interface对象

var x float64 = 3.4
v := reflect.ValueOf(x)

var y float64 = v.Interface().(float64)
fmt.Println("value:", y)

// value: 3.4

1.3. 反射第三定律
反射对象可修改,但value必须是可设置的

var x float64 = 3.4
v := reflect.ValueOf(x) // v的类型是reflect.Value 不可寻址

v.SetFloat(7.1) // Error: will panic
// panic: reflect: reflect.Value.SetFloat using unaddressable value

vp := reflect.ValueOf(&x)
vp.Elem().SetFloat(7.1)
fmt.Println("x:", v.Elem().Interface())

// x : 7.1
上例中,传入reflect.ValueOf()函数的其实是x的值,而非x本身。即通过v修改其值是无法影响x的,也即是无效 的修改,所以golang会报错。
想到此处,即可明白,如果构建v时使用x的地址就可实现修改了,但此时v代表的是指针地址,我们设置的是指针所 指向的内容,也即我们想要修改的是 *v 。 那怎么通过v修改x的值呢?
reflect.Value 提供了 Elem() 方法,可以获得指针向指向的 value 。
反射对象是map 和 pointer 时例外,因为他们已经是指针

{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(&a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.Elem().SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}
{
// map
a := map[int]string{
1: "foo1",
2: "foo2",
}
val := reflect.ValueOf(a)
key3 := reflect.ValueOf(3)
val3 := reflect.ValueOf("foo3")
val.SetMapIndex(key3, val3)
fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3]
}

{
// struct
a := FooStruct{}
val := reflect.ValueOf(&a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) // {foo2}
}
{
// pointer
a := &FooPointer{}
val := reflect.ValueOf(a)
val.Elem().FieldByName("A").SetString("foo2")
fmt.Println(a) //&{foo2}
}

  1. 反射类型reflect.Type和reflect.Value拥有的方法

2.1. Type方法
适用于reflect.Value
此方法返回reflect.Value拥有的类型

x := 6.4
v := reflect.ValueOf(x)
fmt.Println(v.Type())
// float64

2.2. Kind方法
适用于reflect.Type和reflect.Value
此方法返回存储项的常量类型
reflect.Float64
reflect.Uint
...

x := 6.4
v := reflect.ValueOf(x)
t := reflect.TypeOf(x)
fmt.Println(reflect.Float64 == v.Kind())
fmt.Println(reflect.Float64 == t.Kind())

2.2.* Kind方法注意事项

Kind方法返回的是reflect.Value的底层类型,并不像Type方法返回的是静态类型
eg..

type MyFloat float64
var flt MyFloat = 6.4

v := reflect.ValueOf(flt)
t := reflect.TypeOf(flt)

fmt.Println(v.Type(), v.Kind()) // main.MyFloat float64

if reflect.Float64 == t.Kind() {
fmt.Println(v.Float()) // 6.4
}

在示例中,reflect.Value的Type方法返回的是静态类型 main.MyFloat ,Kind方法返回的是底层类型float64.

相关文章
|
3月前
|
Go
Go to Learn Go之反射
Go to Learn Go之反射
44 8
|
2月前
|
安全 Java Go
【Golang入门】简介与基本语法学习
Golang语言入门教程,介绍了Go语言的简介、基本语法、程序结构、变量和常量、控制结构、函数、并发编程、接口和类型、导入包、作用域以及错误处理等关键概念,为初学者提供了一个全面的学习起点。
31 0
|
4月前
|
JSON 人工智能 Go
go 反射的常见用法
go 反射的常见用法
46 4
|
4月前
|
机器学习/深度学习 存储 人工智能
Golang bytes 包学习
Golang bytes 包学习
26 3
|
5月前
|
安全 算法 程序员
在go语言中使用泛型和反射
【7月更文挑战第8天】本文介绍go支持泛型后,提升了代码复用,如操作切片、映射、通道的函数,以及自定义数据结构。 泛型适用于通用数据结构和函数,减少接口使用和类型断言。
129 1
在go语言中使用泛型和反射
|
5月前
|
编译器 Go C语言
通过例子学习在golang中调试程序
【7月更文挑战第4天】Go语言支持使用cgo进行汇编调试,官方文档在golang.org/doc/asm。注意,调试Go运行时可能遇到变量不可用或行号错误,需谨慎使用step命令。
74 1
通过例子学习在golang中调试程序
|
4月前
|
存储 人工智能 Go
golang 反射基本原理及用法
golang 反射基本原理及用法
30 0
|
4月前
|
存储 缓存 人工智能
深入理解 go reflect - 反射为什么慢
深入理解 go reflect - 反射为什么慢
42 0
|
4月前
|
存储 人工智能 JSON
深入理解 go reflect - 反射基本原理
深入理解 go reflect - 反射基本原理
57 0
|
4月前
|
JavaScript 前端开发 Go
Go中使用反射的动态方法调用
Go中使用反射的动态方法调用