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

简介: GO语言的学习与记录,第九篇:通过一个例子了解通过反射进行实例化。内容用到了接口、结构体和JSON互转、反射的类型和实例化的内容

theme: healer-readable

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

初心是记录和总结,自己学习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

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

image.png
它相当于:
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()
}
目录
相关文章
|
2月前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
95 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
2月前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
48 7
|
2月前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
4天前
|
监控 Linux PHP
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
【02】客户端服务端C语言-go语言-web端PHP语言整合内容发布-优雅草网络设备监控系统-2月12日优雅草简化Centos stream8安装zabbix7教程-本搭建教程非docker搭建教程-优雅草solution
51 20
|
2天前
|
存储 监控 算法
探秘员工泄密行为防线:基于Go语言的布隆过滤器算法解析
在信息爆炸时代,员工泄密行为对企业构成重大威胁。本文聚焦布隆过滤器(Bloom Filter)这一高效数据结构,结合Go语言实现算法,帮助企业识别和预防泄密风险。通过构建正常操作“指纹库”,实时监测员工操作,快速筛查可疑行为。示例代码展示了如何利用布隆过滤器检测异常操作,并提出优化建议,如调整参数、结合日志分析系统等,全方位筑牢企业信息安全防线,守护核心竞争力。
|
10天前
|
Go C语言
Go语言入门:分支结构
本文介绍了Go语言中的条件语句,包括`if...else`、`if...else if`和`switch`结构,并通过多个练习详细解释了它们的用法。`if...else`用于简单的条件判断;`if...else if`处理多条件分支;`switch`则适用于基于不同值的选择逻辑。特别地,文章还介绍了`fallthrough`关键字,用于优化重复代码。通过实例如判断年龄、奇偶数、公交乘车及成绩等级等,帮助读者更好地理解和应用这些结构。
34 14
|
2月前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
124 71
|
2月前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
121 67
|
24天前
|
存储 监控 算法
内网监控系统之 Go 语言布隆过滤器算法深度剖析
在数字化时代,内网监控系统对企业和组织的信息安全至关重要。布隆过滤器(Bloom Filter)作为一种高效的数据结构,能够快速判断元素是否存在于集合中,适用于内网监控中的恶意IP和违规域名筛选。本文介绍其原理、优势及Go语言实现,提升系统性能与响应速度,保障信息安全。
27 5
|
1月前
|
算法 安全 Go
Go语言中的加密和解密是如何实现的?
Go语言通过标准库中的`crypto`包提供丰富的加密和解密功能,包括对称加密(如AES)、非对称加密(如RSA、ECDSA)及散列函数(如SHA256)。`encoding/base64`包则用于Base64编码与解码。开发者可根据需求选择合适的算法和密钥,使用这些包进行加密操作。示例代码展示了如何使用`crypto/aes`包实现对称加密。加密和解密操作涉及敏感数据处理,需格外注意安全性。
45 14

热门文章

最新文章