Golang Go语言中无法通过反射将 interface{} 转换成结构体的问题

最近写了个小 demo ,实现通过反射将结构体数组,转换成结构实现的接口类型的数组,到这部分没有问题。但是当打算从接口数组转换回结构结构体数组的时候,却转换失败,不知道原因是什么

代码见 https://go.dev/play/p/ajYUDEs6ssQ

如果不是通过反射,是可以通过类型断言将接口类型转换成底层的结构体,因为 go 的接口类型保存有底层结构的类型信息,但是通过反射却无法转换。

本来打算看看 go 反射的底层实现找找原因,但是水平有限没找到……有大手子可以帮忙解解惑吗?


Golang Go语言中无法通过反射将 interface{} 转换成结构体的问题

更多关于Golang Go语言中无法通过反射将 interface{} 转换成结构体的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

12 回复

因为转换成 Stringer 的时候 box 了一次,底层类型变成了 interface 类型。
加个检查,对 interface 解包
if fe.Kind() == reflect.Interface {
fe = fe.Elem()
}

更多关于Golang Go语言中无法通过反射将 interface{} 转换成结构体的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


reflect.Value.Convert 支持 interface -> interface, 具体类型 -> interface 的转换, 不支持 interface 的转换

可以将 te.Set(fe.Convert(te.Type())) 修改为 te.Set(reflect.ValueOf(fe.Interface())), 这样就可以过了

不支持 interface -> 具体类型的转换, 补充一下

可以理解为,当Data 传给 reflect.ValueOf 时,做了Data->interface{}的转换,然后 convert 函数里,做的是 interface{} -> fmt.Stringer 的转换。这样 fmt.Stringer 的底层类型就变成了 interfaces{},*Data 的信息是包在了 interface{}里,fmt.Stringer 中是没法直接获取到?

哪里可以了解下为什么会有不支持 interface -> 具体类型转换?是 go 的类型系统设计的原因吗?

fmt.Printf("%+v", ss) 可以看出,你两个值是 Nil

#6 哦不是,我的问题

#5

reflect/values.go

if implements(dst, src) {
if src.Kind() == Interface {
return cvtI2I
}
return cvtT2I
}

reflect/types.go

// implements reports whether the type V implements the interface type T.
func implements(T, V *rtype) bool {
if T.Kind() != Interface {
return false
}

}

我猜测是这样的,Convert 的时候两个 Type 进行转换,前者是 fmt.Stringer 后者是 Data, Data => fmt.Stringer 可以因为实现了接口,但 fmt.Stringer => Data 不行, 如果两个被 Interface 抹掉了类型就会取 ValueType, 那两个底层 ValueType 都是 *Data 所以可以

原理上面已经说的差不多了,提醒下范型已经发布可用了,op 这个 demo 正好就是范型的用处

interface 是一个 fat pointer ,一个指向值,一个指向类型。正常情况下你把某个类型赋值给 interface (隐式 box )、或把一个 interface 转换成另一个 interface 是不会改变它的具体值和类型的。而 reflect.ValueOf 则是把一个 interface 里的值拿出来,并提供一些 API 。
但是你 Convert 的时候把一个具体类型转换成 interface ,这里是对其进行了一次原地打包的。
转换前:
( Data 值,Data 类型)
转换后:
( Stringer 值,Stringer 类型)
其中 Stringer 值为 ( Data 值,Data 类型)

确实这个老问题用泛型解决正好,已经准备将项目最低的版本设置为 1.18 了

在Go语言中,使用反射(reflect包)将interface{}类型转换为结构体确实存在一些挑战,但并非完全无法实现。关键在于要正确理解和使用反射API。

首先,确保你持有的是指向具体结构体实例的interface{}。反射操作通常基于类型信息(reflect.Type)和值信息(reflect.Value)。要转换interface{}为结构体,你需要:

  1. 获取反射值:使用reflect.ValueOf获取interface{}的反射值。
  2. 类型断言:通过Type.Kind()检查是否为结构体(reflect.Struct)。
  3. 类型转换:如果类型匹配,可以使用reflect.Value.Interface().(YourStructType)进行类型断言转换回具体结构体类型。但注意,这一步在编译时类型不安全,需要在运行时确保类型正确。

一个常见问题是,如果interface{}实际包含一个指针类型(如*YourStructType),直接转换会失败。这种情况下,你需要先检查反射值的Kind()是否为reflect.Ptr,然后获取其指向的元素(Elem()),再进行结构体类型的断言。

示例代码可能如下:

v := reflect.ValueOf(someInterface)
if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct {
    if s, ok := v.Elem().Interface().(YourStructType); ok {
        // 成功转换
    }
}

总之,虽然通过反射进行类型转换在Go中相对复杂,但通过仔细检查类型信息和正确使用反射API,是可以实现的。

回到顶部