「Golang反射实战2」 使用接口实现 深拷贝(deepcopy)
「Golang反射实战2」 使用 接口(interface) 实现 深拷贝(deepcopy)
大家好, 我是老麦, 一个运维老兵, 先专注于 Golang,DevOps,云原生基础建设。
原文链接: https://typonotes.com/posts/2023/03/20/golang-reflect-interface-deeopcopy/
interface 接口 deepcopy 的实现
对于 接口 interface{}
就稍微麻烦一点了。 由于 接口 是一组方法的集合, 也就意味着
- 接口的 底层结构体 是不定的。
- 无法直接获取 底层结构体 的字段数据。
这时可以通过使用 反射 reflect.New()
创建对象。
mohae/deepcopy - Github 就是使用的这种方式
https://github.com/mohae/deepcopy/blob/c48cc78d482608239f6c4c92a4abd87eb8761c90/deepcopy.go#L39
deepcopy 库中一样通过 反射递归 实现复制, 是为了兼容更多的情况。而在自己实现编码的时候, 大部分情况的是可控的, 实现方式可以适当简化, 不用与 deepcopy 完全相同。
1. 通过反射创建零值接口对象
func deepcoper(op Operator) Operator {
// 1. 获取 反射类型
rt := reflect.TypeOf(op)
// 2. 获取真实底层结构体的类型
rtype := deRefType(rt)
// 3. reflect.New() 创建反射对象,并使用 Interface() 转真实对象
opc := reflect.New(rtype).Interface()
// 4. 断言为 operator
return opc.(Operator)
}
func deRefType(typ reflect.Type) reflect.Type {
for typ.Kind() == reflect.Ptr {
typ = typ.Elem()
}
return typ
}
需要注意的是, 通过上述方式创建的出来的新对象
- 依然 不知道新接口底层结构体是什么, 也并不需要关心, 接口中心本身就是在 相同的方法实现 上。
- 接口底层结构体中所有字段值为 零值, 可能需要必要的初始化,否则直接使用可能
panic
, 例如结构体中存在 指针类型对象 。
通常这种情况, 在自己写代码的时候,可以增加一个 初始化方法 。
2. 使用接口断言进行初始化
在实现了初始化方法之后, 可以再定义一个接口。通过断言转化为新接口, 调用初始化方法。
注意:
Operator
是某个接口, 具体实现方法,我们可以不需要理解。
func deepcoper(op Operator) Operator {
rt := reflect.TypeOf(op)
rtype := deRefType(rt)
opc := reflect.New(rtype).Interface()
// 3.1. 使用断言转化新接口, 初始化底层对象
if opcInit, ok := opc.(OperatorIniter); ok {
opcInit.SetDefaults()
}
return opc.(Operator)
}
type OperatorIniter interface {
SetDefaults()
}
上面代码中, 我们将通过断言, 将 Operator 转称 OperatorIniter。一定要 熟悉接口的断言操作。
这样, 我们就可以通过调用 SetDefaults()
方法, 对 未知 的底层结构体进行初始化了。
3. 使用反射调用方法进行初始化
在不增加新接口的情况下, 可以在反射创建的过程中 判断初始化方法的存在, 并调用 进行初始化。
func deepcoper(op Operator) Operator {
rt := reflect.TypeOf(op)
rtype := deRefType(rt)
ropc := reflect.New(rtype)
// 3.2 使用反射 call method 初始化
method := ropc.MethodByName("SetDefaults")
if method.IsValid() && !method.IsZero() {
method.Call(nil)
}
opc := ropc.Interface()
return opc.(Operator)
}
搞点看起来高级的: 接口初始化工厂
上述代码中都是直接对接口对象进行的操作。搞一个 struct 创建并初始化接口, 可以携带和组织更多的信息。
func NewOperatorFactory(op Operator) *OperatorFactory {
opfact := &OperatorFactory{}
opfact.Type = deRefType(reflect.TypeOf(op))
// opfact.Operator = op
return opfact
}
type OperatorFactory struct {
Type reflect.Type
Operator Operator
}
func (o *OperatorFactory) New() Operator {
oc := reflect.New(o.Type).Interface().(Operator)
return oc
}
相关文章
- 微信公众平台开发接口_小程序注册好了为什么搜索不到
- GoLang接口---中
- GoLang接口---下
- 大厂都在做的jmeter接口自动化测试登峰造极的jmeter实现接口自动化测试
- intellij idea 怎么查看方法在哪里被调用_进入接口
- 如何使用接口
- java面向接口编程的例子_大二java期末考试试题
- EasyCVR调用接口获取RTSP/RTMP播放地址,出现RTSP流地址重复是什么原因?
- 【Java集合框架】篇四:Set接口
- 太强了,一个注解搞定接口返回数据脱敏
- 重磅推荐!12.2K标星的免费接口合集,API的搬运工,开发者的福利!
- Map接口
- 批量上传GPT知识库,前端elementui的upload上传组件,后端Golang的上传接口实现
- 使用Golang快速连接MySQL数据库(golang连接mysql)
- MySQL中的接口是什么简单解析和应用(mysql中什么是接口)
- 接口接入实现Redis集群的可靠性(接口接入redis集群)
- java比较器Comparable接口与Comaprator接口的深入分析