反射
反射是指在程序运行时来检查变量的结构
- 对于接口来说才有反射概念
接口
因为有接口才有反射,在理解反射前要理解下接口
- 对于每一个接口变量,它都在底层保存了一对值(
pair
):接口的值和类型。 - 接口变量的静态类型在编译期间就已经确定,并且只有一个静态类型。
var r io.Reader
r = os.Stdin
r = bufio.NewReader(r)
r = new(bytes.Buffer)
这里的变量r
的静态类型只是io.Reader
,正如在编译期间编译器根据这个静态变量类型来检查代码中是否有类型错误
- 我们常常会用
interface {}
来做函数的变量,来接收所有类型的值,因为所有的变量都实现了interface {}
的方法
package main
import (
"fmt"
)
func main() {
getVal(1) // 1
getVal("a") // "a"
getVal(true) // true
}
func getVal(a interface{}) {
fmt.Println(a)
}
正如getVal
的函数接收所有类型的变量,这里的接口变量a
的类型是不确定的,可以说它的类型是动态的,当需要获取外部传进来的值的类型时,反射可以解决这个问题。
反射核心概念
go中实现反射的包是reflect
,其核心是reflect.Value
和reflect.Type
,我们分别可以通过reflect.ValueOf
和reflect.TypeOf
得到他们。
package main
import (
"fmt"
"reflect"
)
func main() {
v := reflect.TypeOf(3)
fmt.Println(v) // int
}
这里通过reflect.TypeOf
获取了变量3
的类型,但之前不是说对于接口才有反射吗,其实在reflect.TypeOf
源码中接收的就是空接口,所以任何的值在reflect.TypeOf
中被转换成了接口。
// TypeOf returns the reflection Type that represents the dynamic type of i.
// If i is a nil interface value, TypeOf returns nil.
func TypeOf(i interface{}) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
package main
import (
"fmt"
"reflect"
)
func main() {
type MyInt int
var m MyInt = 5
getVal(m)
// main.MyInt
// int
getVal(1)
// int
// int
}
func getVal(a interface{}) {
t := reflect.TypeOf(a)
fmt.Println(t)
fmt.Println(t.Kind())
}
reflect.TypeOf
总是返回运行时变量的类型,可能变量在运行时值被替换,它的实时的类型就可以通过reflect.TypeOf
获取。
对于Kind()
方法可以帮助我们获取到被type
关键字封装前的类型。
调用这个方法会返回reflect.Value
,reflect.Value
是个结构体,虽然在结构体内很多私有属性,但也提供了许多获取类型信息的方法。
package main
import (
"fmt"
"reflect"
)
func main() {
type MyInt int
var m MyInt = 5
getVal(m)
// 5
// main.MyInt
getVal(1)
// 1
// int
}
func getVal(a interface{}) {
t := reflect.ValueOf(a)
fmt.Println(t)
fmt.Println(t.Type())
}
reflect.ValueOf
也可以通过Type()
来获取reflect.Type
即变量的实时类型。
反射提供了强大的类型检查功能,但还是要谨慎使用反射:
- 反射会降低性能,尽量在性能要求不高的地方使用
- 反射是在运行时检查类型和值,编译期间的类型问题在编译中就会被发现,但使用反射可能会有很多意想不到的问题
- 反射的代码并不好维护