本文讲解如何通过反射正确获取接口中指针所指向的值(即深拷贝其内容),而非仅复制指针地址,并分析其性能影响与使用注意事项。
本文讲解如何通过反射正确获取接口中指针所指向的值(即深拷贝其内容),而非仅复制指针地址,并分析其性能影响与使用注意事项。
在 Go 中,interface{} 本身不存储值,而是存储类型信息和底层数据(包括指针地址)。当你将一个指针(如 *str)赋值给 interface{},该接口保存的是该指针的地址副本,而非其所指向结构体的副本。因此,直接调用 reflect.ValueOf(a).Interface() 得到的仍是原指针,修改原结构体字段后,通过该接口访问到的字段也会变化——这本质上仍是共享同一内存地址。
要真正“复制”指针所指向的值(即实现值语义的克隆),需借助 reflect.Indirect:它会递归解引用指针、切片、映射等间接类型,最终返回最内层可寻址的值(reflect.Value)。随后调用 .Interface() 即可获得该值的一个独立副本(注意:是浅拷贝;若结构体内含指针或引用类型,其内部仍共享)。
以下是修正后的完整示例:
package main
import (
"fmt"
"reflect"
)
type str struct {
field string
}
func main() {
s := &str{field: "astr"}
a := interface{}(s)
// 关键:先解引用,再取值副本
v := reflect.Indirect(reflect.ValueOf(a))
b := v.Interface() // b 是 *str 所指向的 str 值的副本(类型为 str)
s.field = "changed field"
fmt.Printf("a (via interface{}): %+v\n", a) // &{field:"changed field"}
fmt.Printf("b (cloned value): %+v\n", b) // {field:"astr"}
}
⚠️ 重要注意事项:
- reflect.Indirect 要求传入的 reflect.Value 必须是可寻址的指针类型;若传入非指针(如 str 值本身),它会直接返回原值。
- 此方式生成的是浅拷贝:若结构体字段包含 *T、[]T、map[K]V 等引用类型,副本中的这些字段仍与原值共享底层数据。如需深拷贝,应使用专用库(如 github.com/jinzhu/copier 或 gob 序列化)。
- 性能方面:反射操作(尤其是 reflect.ValueOf + Indirect + Interface())比直接解引用(*s)慢一个数量级以上,涉及类型检查、内存分配和运行时调度。仅在无法静态确定类型、且必须动态克隆值的场景下使用;日常开发中优先采用显式赋值、构造函数或 Clone() 方法。
总结:reflect.Indirect(reflect.ValueOf(x)).Interface() 是获取接口中指针所指值副本的标准反射路径,它解决了“复制内容而非地址”的核心需求,但需权衡可读性、安全性与性能——切勿在热路径中滥用。











