大家好, 我是老麦, 一个运维老兵, 先专注于 Golang,DevOps,云原生基础建设。

原文链接: https://typonotes.com/posts/2023/03/20/golang-reflect-interface-deeopcopy/

interface 接口 deepcopy 的实现

interface{}
  1. 接口的 底层结构体 是不定的。
  2. 无法直接获取 底层结构体 的字段数据。
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
}