赞
踩
interface回顾
首先我们简单的回顾一下interface的结构,总体上是:
细分下来分为有函数的 iface和无函数的 eface(就是 interface{});
无函数的 eface
有函数的 iface
Go语言中,每个变量都有唯一个静态类型,这个类型是编译阶段就可以确定的。有的变量可能除了静态类型之外,还会有动态混合类型。
例如以下例子:
//带函数的interface
var r io.Reader
tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0)
if err != nil {
return nil, err
}
r = tty
//不带函数的interface
var empty interface{}
empty = tty
有函数的 iface的例子
我们一句一句来看:第1行, varr io.Reader
第4行至第7行就是简单的赋值,得到一个 *os.File的实例,暂且不看了。
最后一句 r=tty
无函数的 eface的例子
我们接着往下看, varemptyinterface{}
最后是 empty=tty
但是记住:虽然有动态混合类型,但是对外"表现"依然是静态类型。
在编译时
不知道类型的情况下,通过反射机制可以获取对象的类型、值、方法
甚至`动态改变对象的成员,这就是反射机制
//接口数据 =====》 反射对象
1. Reflection goes from interface value to reflection object.
//反射对象 ===> 接口数据
2. Reflection goes from reflection object to interface value.
// 倘若数据可更改,可通过反射对象来修改它
3. To modify a reflection object, the value must be settable.
法则一:接口=》反射对象
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
fmt.Println("type:", t) //type: float64
fmt.Println("value:", v.String()) //value: <float64 Value>
fmt.Println("type:", v.Type()) // type: float64
fmt.Println("kind is float64:", v.Kind() == reflect.Float64) //kind is float64: true
fmt.Println("value:", v.Float()) //value: 3.4
}
由代码17行可以看出:Value还可以获取到当前数据值的 Type。所以,法则一的图应为:
(1)通过v.String()获取类型
(2)通过v.Type()获取类型
package main
import (
"fmt"
"reflect"
)
type temprature int
func main() {
var temp interface{} = temprature(5)
fmt.Printf("temprature is %d\n", temp.(temprature))
itype := reflect.TypeOf(temp)
ivalue := reflect.ValueOf(temp)
fmt.Printf("%v: %v", itype, ivalue)
reflect.TypeOf()可以获取interface里值的类型
与interface中name.(type)
效果一致。
法则二:反射对象=》接口
注意 Type是没法逆向转换
的
package main
import (
"fmt"
"reflect"
)
func main() {
var x float64 = 3.4
t := reflect.TypeOf(x)
v := reflect.ValueOf(x)
...
o := v.Interface().(float64) // 法则2代码
fmt.Println(o)
}
法则三:修改原数据的值
法则三是说:通过反射对象,可以修改原数据中的内容。
这里说的反射对象,是指 Value,毕竟 Type只是表示原数据的类型相关的内容,而 Value是对应着原数据对象本身。
如何通过反射对象来修改原数据对象的值呢?
如何才能可以通过反射对象来修改原数据对象的值或者说为什么不能设置呢?
原因简单且纯粹:在Go中,任何函数的参数
都是值的拷贝
,而非原数据
。
反射函数 reflect.ValueOf()
也不例外。我们目前得到的反射对象,都是原对象的copy的反射对象
,而非原对象本身
,所以不可以修改到原对象
;即使可以修改,修改一个传参时候的副本,也毫无意义,不如报错儿。Go反射第三法则中的制定的 settable属性就由此而来,还延伸出了类似于 CanSet()的方法。
那如何修改呢?
(1)首先,在Go中要想让函数“有副作用“,传值必须传指针类型的。
...
var x float64 = 3.4
v := reflect.ValueOf(&x)
...
(2)此时还不行,因为这样反射对象对应的是原数据对象的指针类型
,必须要拿到当前类型的值类型(*v)
,如何做?Go提供了另外一个方法 Elem()
...
var x float64 = 3.4
v := reflect.ValueOf(&x)
p := v.Elem()
fmt.Println(p.CanSet()) // true
p.SetFloat(7.1)
fmt.Println(x) // 7.1
不难发现,go的反射和interface在结构上是如此的相近!都分为两部分:一部分是 Type一部分是 value。
反射会不会是比着interface来实现的?
反射是什么意思?反射的意思是在运行时
,能够动态
知道给定数据对象的类型和结构
,并有机会修改它!现在一个数据对象,如何判断它是什么结构?数据interface中保存有结构数据呀,只要想办法拿到该数据对应的内存地址,然后把该数据转成interface,通过查看interface中的类型结构,就可以知道该数据的结构了呀~ 其实以上就是Go反射通俗的原理。
我们学习反射,其实就是学习如何使用原变量
,去取得reflect.Type
或者reflect.Value
这种反射对象;再使用这个反射对象Type
以及Value
,反过来对原变量
进行操作
Type与Kind的区别是什么?
Type
是类型
,Kind
是类别
,听起来有点绕,他们之间的关系为Type
是Kind的子集
如果变量是基本类型
,那么Type与Kind得到的结果是一致的,比如变量为int类型,Type与Kind的值相等,都为int
但当变量为结构体时,Type与Kind的值就不一样了
func main() {
var emp Employee
emp = Employee{
Name: "naonao",
Age: 99,
}
rVal := reflect.ValueOf(emp)
log.Printf("Kind is %v ,Type is %v",
rVal.Kind(),
rVal.Type())
// Kind is struct ,Type is main.Employee
}
可以看到,Kind
的值是struct
,而Type
的值是包名.Employee
反射如何在变量与reflect.Value之间切换?
变量
可以转换成interface{}
之后,再转换成reflect.Value
类型,既然空接口可以转换成Value类型,那么自然也可以反过来转换成变量
用个表达式来表示,就如下所示
变量<----->interface{}<----->reflect.Value
利用空接口
来进行中转,这样变量
与Value
之间就可以实现互相转换了
如何使用反射获取变量本身的值?
这里我们要注意一下,reflect.ValueOf()
得到的值是reflect.Value
类型,并不是变量本身的值
var num = 1
rVal := reflect.ValueOf(num)
log.Printf("num is %v", num + rVal)
这段代码会报错
> invalid operation: num + rVal (mismatched types int and reflect.Value)
很明显,rVal是属于reflect.Value
类型,不能与int
类型相加
那怎样才能获得它本身的值呢?
如果是基本类型,比如var num int,那么使用reflect包里提供的转换方法即可reflect.ValueOf(num).Int()
或者是float,那就调用reflect.ValueOf(num).float()
,如果是其它的基本类型,需要的时候去文档里面找找即可.
但如果是我们自己定义的结构体,因为reflect
包无法确定我们自己定义了什么结构体,所以本身并不会带有结构体转换的方法,那么我们只能通过类型断言
来进行转换
也就是上面说的,利用空接口
进行中转,再利用断言
进行类型转换,可以看如下代码示例
// Employee 员工
type Employee struct {
Name string
Age int
}
func main() {
emp := &Employee{
Name: "naonao",
Age: 99,
}
reflectPrint(emp)
}
func reflectPrint(v interface{}) {
rVal := reflect.ValueOf(v) // 获取reflect.Value
iV := rVal.Interface() // 利用空接口进行中转
empVal, ok := iV.(*Employee) // 利用断言转换
if ok {
// 如果成功转换则打印结构体
log.Print(empVal)
}
}
通过反射来修改变量
先来看看代码如何实现
func main() {
var num = 1
modifyValue(&num)// 传递地址
log.Printf("num is %v", num)// num is 20
}
func modifyValue(i interface{}) {
rVal := reflect.ValueOf(i)
rVal.Elem().SetInt(20)
}
细心的你肯定发现了一点异常,函数接收的参数不再是值
了,而是接受了一个指针地址
改变值的时候,先调用了Elem()
方法,再进行了一个SetInt()
的操作
为什么直接传值不行呢?因为reflect包中提供的所有修改变量值的方法
,都是对指针进行的操作
那为什么还要先使用Elem()呢?因为Elem()
的作用,就是取得指针地址所对应的值
,取到值了,我们才能对值进行修改
1、类型
反射通过reflect.TypeOf()
获取类型,interface也可以通过.(type)
获取类型
区别:
(1)reflect.TypeOf()的返回值是reflect.Type,interface中.(type)
获取的是一个包含类型结构体。
(2)reflect.TypeOf()可以获取一个结构体中的字段类型,interface只能获取结构体类型,无法获取字段类型。
2、值
(1)interface取得就是值本身
empVal :=interface_name.(*Employee)
(2)reflect.ValueOf()的返回值是reflect.Value,必须通过interface进行转换获取自身的值。
rVal := reflect.ValueOf(v) // 获取reflect.Value
iV := rVal.Interface() // 利用空接口进行中转
empVal, ok := iV.(*Employee) // 利用断言转换
3、函数
reflect可以通过获取的结构体值获取结构体实现的方法,并执行。
method = rVal.MethodByName(methodByName)
result := method.Call(params)
// Employee 员工
type Employee struct {
Name string `json:"emp_name"`
Age int `json:"emp_age"`
Sex int
}
// GetSum 返回两数之和
func (e *Employee) GetSum(n1, n2 int) int {
return n1 + n2
}
// Set 接受值,给结构体e赋值
func (e *Employee) Set(name string, age, sex int) {
e.Name = name
e.Age = age
e.Sex = sex
}
// Print 打印结构体*Employee
func (e *Employee) Print() {
log.Print("======Start======")
log.Print(e)
log.Print("======End======")
}
// GetStruct 获取结构体的字段及tag
func GetStruct(i interface{}) {
rType := reflect.TypeOf(i)
rVal := reflect.ValueOf(i)
kd := rVal.Kind()
// 如果是传进来的是指针类型
// 则获取指针值
if kd == reflect.Ptr {
rType = rType.Elem()
rVal = rVal.Elem()
kd = rVal.Kind()
}
if kd != reflect.Struct {
log.Panicf("Kind is %v not struct ", kd)
}
// 获取结构体的字段数
sNum := rVal.NumField()
log.Printf("Struct has %v fields ", sNum)
// 遍历结构体的所有字段
for i := 0; i < sNum; i++ {
log.Printf("Field %d value is %v", i, rVal.Field(i))
// 获取Struct的tag,使用Type类型获取
tag := rType.Field(i).Tag.Get("json")
if tag == "" {
log.Printf("Field %d hasn't tag %v ", i, tag)
continue
}
log.Printf("Field %d tag is %v ", i, tag)
}
}
// CallMethod 调用结构体方法
// i : 传入的struct
// methodByName : 调用结构体的方法名
func CallMethod(i interface{}, methodByName string) {
rVal := reflect.ValueOf(i)
rType := reflect.TypeOf(i)
log.Printf("Type is %v Kind is %v", rType, rType.Kind())
// 获取结构体有多少个方法
numOfMethod := rVal.NumMethod()
log.Printf("Struct has %d method", numOfMethod)
// 声明Value数组
var params []reflect.Value
// 声明一个Value类型,用于接收方法
var method reflect.Value
if methodByName == "GetSum" {
// 调用方法时的参数
params = append(params, reflect.ValueOf(10))
params = append(params, reflect.ValueOf(88))
}
if methodByName == "Set" {
// 调用方法时的参数
params = append(params, reflect.ValueOf("闹闹吃鱼"))
params = append(params, reflect.ValueOf(18))
params = append(params, reflect.ValueOf(0))
}
// 获取方法
method = rVal.MethodByName(methodByName)
if !method.IsValid() {
// 如果结构体不存在此方法,输出Panic
log.Panic("Method is invalid")
}
result := method.Call(params)
if len(result) > 0 {
// 如果函数存在返回值,则打印第一条
log.Println("Call result is ", result[0])
}
}
// ModifyField 修改字段值
func ModifyField(i interface{}, filedName string) {
rVal := reflect.ValueOf(i)
filed := rVal.Elem().FieldByName(filedName)
if !filed.IsValid() {
log.Panic("filedName is invalid")
}
filed.SetString("闹闹")
}
// Bridge 适配器
// 可以实现调用任意函数
func Bridge(call interface{}, args ...interface{}) {
var (
function reflect.Value
inValue []reflect.Value
)
n := len(args)
// 将参数转换为Value类型
inValue = make([]reflect.Value, n)
for i := 0; i < n; i++ {
inValue[i] = reflect.ValueOf(args[i])
}
// 获得函数的Value类型
function = reflect.ValueOf(call)
// 传参,调用函数
function.Call(inValue)
}
测试
func TestBridge(t *testing.T) {
call1 := func(v1, v2 int) {
log.Println(v1, v2)
}
call2 := func(v1, v2 int, str string) {
log.Println(v1, v2, str)
}
Bridge(call1, 1, 2)
Bridge(call2, 2, 3, "callTest")
}
// CreateStruct 使用反射创建结构体
// 并给结构体赋值
func CreateStruct(i interface{}) *Employee {
var (
structType reflect.Type
structValue reflect.Value
)
// 获取传入结构体指向的Type类型
structType = reflect.TypeOf(i).Elem()
// 创建一个结构体
// structValue持有一个指向类型为Type的新申请的指针
structValue = reflect.New(structType)
// 转换成我们要创建的结构体
modle := structValue.Interface().(*Employee)
// 取得structValue指向的值
structValue = structValue.Elem()
// 给结构体赋值
structValue.FieldByName("Name").SetString("闹闹吃鱼")
structValue.FieldByName("Age").SetInt(100)
return modle
}
学习链接:https://blog.csdn.net/i6448038/article/details/104337837
https://www.jianshu.com/p/9816a7a551cd
https://www.jianshu.com/p/32e4cf8ffffb
https://www.jianshu.com/p/53adb1e92710
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。