Go语言接口多态问题:实现同一接口的结构体切片无法作为接口切片传入
问题根本原因
Go语言的类型系统不允许[]Person1/[]Person2直接转换为[]Loggable,和Java支持数组协变的设计不同:
- Go中切片的类型是整体独立的,
[]Loggable不是“所有实现Loggable的结构体的切片”的父类型,二者的内存存储结构完全不同:[]Loggable每个元素存储的是(类型指针, 值指针)二元组,而[]Person1每个元素直接存储Person1结构体的值,无法直接隐式转换。
符合Go规范的实现方案
分两种场景选择:
方案1:泛型实现(Go 1.18+ 优先推荐)
直接给日志函数增加泛型约束,不需要做任何切片转换,无额外性能开销,是目前最符合Go开发规范的写法:
// 增加泛型参数T,约束为必须实现Loggable接口 func performLogging[T Loggable](logger LoggingPackage.Logger, loggingList []T){ for _, person := range loggingList { logger.Logf("Person is %s %s", person.GetFirstName(), person.GetLastName()) } }
修改后原有调用代码不需要任何改动,直接传[]Person1或[]Person2都可以正常编译运行。
方案2:手动转换切片(兼容Go 1.17及更早版本)
如果项目用的是没有泛型的老版本Go,可以手动把结构体切片转换为接口切片再传入:
// 转换[]Person1为[]Loggable func person1ToLoggable(list []Person1) []Loggable { res := make([]Loggable, len(list)) for i, v := range list { res[i] = v } return res } // 转换[]Person2为[]Loggable func person2ToLoggable(list []Person2) []Loggable { res := make([]Loggable, len(list)) for i, v := range list { res[i] = v } return res } func relevantFunction() { personList := handler.PersonRequest() // 根据返回的实际类型调用对应转换函数 switch list := personList.(type) { case []Person1: performLogging(logger, person1ToLoggable(list)) case []Person2: performLogging(logger, person2ToLoggable(list)) } }
内容的提问来源于stack exchange,提问作者nathan.hunt
相关产品推荐
相关产品推荐

