GO语言-09通过例子了解通过反射进行实例化

初心是记录和总结,自己学习Go语言的历程。如果能帮助到你,这是我的荣幸。

反射 - 万变不离其宗

反射能够将我们的程序一下子变得高大上!通过反射可以在程序运行期对程序本身进行访问和修改的能力。最最常见的一个例子就是接收用户传过来的值,将值赋给响应的变量。这种从值 -> 类型的感觉就是反射。举一个对比的例子,之前写程序都是:

var x int = 8

这种都是静态的,定死的变量。在程序运行过程中,永远不会被改变,难道你写程序都是写Demo这种重复且重复的事情吗!!当然不是(虽然学习过程中一直在做这种事情,哈哈)。

那么,B/S架构的程序,现在常用的数据传输是用户通过浏览器输入数据,前端使用JSON作为数据的传输格式,设想一下,我们接收到的不确定的值,如何对它进行封装呢?

结构体 + 反射的案例

俺们知道结构体这种类型的变量,作为多种类型的复合产品,最适合来形容物体多属性的特征。这里比如说还是:

小程在一家宠物店填写了和狗子:小黑的信息,要求宠物店后端将收到的Json进行封装,并输出。

【前提知识1】了解一下JSON和结构体互转

这种都是调用他人的函数库,就不详细介绍啦,了解用法就行。这里使用的是encoding/json,通过 json. 的方式,调用Marshal(转json)或Unmarshal(json转结构体)

结构体转JSON

buf, err := json.Marshal(定义好的结构体变量) //转换为json返回两个结果
if err != nil {
   // 发现错误打印错误,并返回
   fmt.Println("err = ", err)
   return
}
// 输出结果
fmt.Println("json = ", string(buf))

JSON转结构体

// 定义结构体变量
var res 结构体
// 传入结构体变量指针作为第二个参数传入
if err := json.Unmarshal([]byte(jsonstr), &res); err != nil {
   // 发现错误打印错误
   fmt.Println(err)
}
// 输出结果
fmt.Println(res)

【前提知识2】结构体取类型+实例化的方法

反射用的是reflect内置函数库,我们先了解一下取类型和实例化

tp := reflect.TypeOf(x) // 获取到x的类型对象Type
instance := reflect.New(tp) //通过Type获得该类型的值类型反射接口interface

【正篇】定义结构体

type Person struct {
   Name string `json:"name"`
   Age  int    `json:"age"`
}

type Dog struct {
   Name  string `json:"name"`
   Color string `json:"color"`
}

讲解:结构体转义成json的时候,保持名称小写会让程序看的清爽,`json:“name”`,这种用法就是表示在json转义的时候名称按照name这个值来。

【正篇】准备JSON语句模拟我们的数据

func main() {
   personJson := `{"name":"程云来","age":23}`
   dogJson := `{"name":"小黑","color":"黑棕色"}`
}

这个时候,我们最容易想到的是,直接定义两个类型变量,然后通过上面学的JSON和结构体互转的方式,直接转义:

直接定义对象通过方法转义

func main() {
   personJson := `{"name":"程云来","age":23}`
   dogJson := `{"name":"小黑","color":"黑棕色"}`
   

    var person Person
    var dog Dog

    if err := json.Unmarshal([]byte(personJson), &person); err != nil {
       fmt.Println(err)
    }
    fmt.Println(person)

    if err := json.Unmarshal([]byte(dogJson), &dog); err != nil {
       fmt.Println(err)
    }
    fmt.Println(dog)

}

等等,这重复代码太多了吧,而且代码显得不好看,能不能思考一下,通过这个对象调用什么方法的方式就能完成json.Unmarshal

【正篇】加上接口

如果,我们通过Person{}.toStruct的方式,我们就能完成JSON转结构体的动作,这会美化我们的代码,而每个类的toStruct方法应该抽取,利用反射的技术将JSON转结构体的方法,

type JsonToStruct interface {
   toStruct()
}

// 2.实现接口,实现接口中应该将具体的封装操作通过反射的方式抽取出来,避免重复的代码
func (Person) toStruct(jsonStr string) Person {
    // 调用反射的技术
}

func (Dog) toStruct(jsonStr string) Dog {
    // 调用反射的技术
}

【正篇】利用反射完成JSON转结构体

观察JSON转结构体的方法语句:json.Unmarshal([]byte(jsonstr), &res),我们可以发现这里有两个变量:

  • json语句变量:jsonstr
  • 实际对象的指针值

所以方法也应该接收该两个变量,由于我们抽取出两者的共性,而对象类型是不唯一的,所以对象类型接收使用空接口,调用反射的技术实例化对象。

func newInstance(x interface{}, jsonStr string) interface{} {
   tp := reflect.TypeOf(x)
   instance := reflect.New(tp).Interface()
   if err := json.Unmarshal([]byte(jsonStr), &instance); err != nil {
      fmt.Println(err)
   }
   return instance
}

小坑注意!

instance := reflect.New(tp).Interface()该语句为什么要这样写,通过文档得知New方法返回的是Value

反射的New方法

这个Value是Go语言值的反射接口,它并不是一个类型的对象,所以不能直接将范围值使用,解决办法是在后面再调用Interface(),我们也通过文档看看

在这里插入图片描述

它相当于:
Var i interface{} = (v的底层值)

表示使用了一个空接口,接收了New方法实例化的实际对象

【正篇】完善实现接口

func (Person) toStruct(jsonStr string) Person {
   instance := newInstance(Person{}, jsonStr)
   person := instance.(*Person)
   return *person
}

func (Dog) toStruct(jsonStr string) Dog {
   instance := newInstance(Dog{}, jsonStr)
   dog := instance.(*Dog)
   return *dog
}

由于空接口是一种指针类型的数据,所以断言的时候需要加上*Person

完整代码附上

package main

import (
   "encoding/json"
   "fmt"
   "reflect"
)

type Person struct {
   Name string `json:"name"`
   Age  int    `json:"age"`
}

type Dog struct {
   Name  string `json:"name"`
   Color string `json:"color"`
}

// 这里例子是模拟最底层的封装结构体,假设前端传入了一段json字符串,我们需要将它转为对应的struct
func main() {
   personJson := `{"name":"程云来","age":23}`
   dogJson := `{"name":"小黑","color":"黑棕色"}`

   person := Person{}.toStruct(personJson)
   dog := Dog{}.toStruct(dogJson)
   fmt.Println(person)
   fmt.Println(dog)

}

// 发现封装json的代码重复性太高了,进行改造,我们可以考虑封装一个结构体的方法,通过它接收一个json字符串后然后对其进行封装。
// 1.定义接口
type JsonToStruct interface {
   toStruct()
}

// 2.实现接口,实现接口中应该将具体的封装操作通过反射的方式抽取出来,避免重复的代码
func (Person) toStruct(jsonStr string) Person {
   instance := newInstance(Person{}, jsonStr)
   person := instance.(*Person)
   return *person
}

func (Dog) toStruct(jsonStr string) Dog {
   instance := newInstance(Dog{}, jsonStr)
   dog := instance.(*Dog)
   return *dog
}

// 3.将封装的语句抽取封装
func newInstance(x interface{}, jsonStr string) interface{} {
   tp := reflect.TypeOf(x)
   instance := reflect.New(tp).Interface()
   if err := json.Unmarshal([]byte(jsonStr), &instance); err != nil {
      fmt.Println(err)
   }
   return instance
}

// 4.优化
type GetType interface {
   toStruct()
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值