我有一个接口Model
,它由struct Person
实现。
要获取模型实例,我有以下辅助函数:
func newModel(c string) Model {
switch c {
case "person":
return newPerson()
}
return nil
}
func newPerson() *Person {
return &Person{}
}
上述方法允许我返回一个正确输入的Person实例(稍后可以使用相同的方法轻松添加新模型)。
当我尝试做类似的事情来返回一片模型时,我收到一个错误。代码:
func newModels(c string) []Model {
switch c {
case "person":
return newPersons()
}
return nil
}
func newPersons() *[]Person {
var models []Person
return &models
}
抱怨:cannot use newPersons() (type []Person) as type []Model in return argument
我的目标是返回所请求的任何模型类型的片段([]Person
,[]FutureModel
,[]Terminator2000
,w / e)。我缺少什么,以及如何正确实施这样的解决方案?
答案 0 :(得分:81)
这与我刚才回答的问题非常相似:https://stackoverflow.com/a/12990540/727643
简短的回答是你是对的。一块结构不等于结构实现的接口的切片。
[]Person
和[]Model
具有不同的内存布局。这是因为它们是切片的类型具有不同的内存布局。 Model
是一个接口值,这意味着在内存中它的大小是两个单词。一个字为类型信息,另一个为数据。 Person
是一个结构,其大小取决于它包含的字段。要从[]Person
转换为[]Model
,您需要遍历数组并为每个元素执行类型转换。
由于此转换是O(n)操作并且将导致创建新切片,因此Go拒绝隐式执行此操作。您可以使用以下代码明确地执行此操作。
models := make([]Model, len(persons))
for i, v := range persons {
models[i] = Model(v)
}
return models
就像dskinner pointed out一样,你很可能想要一片指针,而不是指向切片的指针。通常不需要指向切片的指针。
*[]Person // pointer to slice
[]*Person // slice of pointers
答案 1 :(得分:8)
这可能是您的返回类型*[]Person
的一个问题,它实际上应该是[]*Person
,以便引用切片的每个索引是对Person
的引用,以及切片[]
本身就是对数组的引用。
查看以下示例:
package main
import (
"fmt"
)
type Model interface {
Name() string
}
type Person struct {}
func (p *Person) Name() string {
return "Me"
}
func NewPersons() (models []*Person) {
return models
}
func main() {
var p Model
p = new(Person)
fmt.Println(p.Name())
arr := NewPersons()
arr = append(arr, new(Person))
fmt.Println(arr[0].Name())
}
答案 2 :(得分:7)
斯蒂芬已经回答了这个问题并且你是初学者,我强调提出建议。
使用go的接口更好的方法是不要让构造函数返回 你可能习惯使用其他语言的接口,比如java,但要有 每个对象的构造函数,因为它们隐式实现了接口。
而不是
newModel(type string) Model { ... }
你应该做
newPerson() *Person { ... }
newPolitician() *Politician { ... }
Person
和Politician
都实现了Model
的方法。
您仍然可以在Person
的任何地方使用Politician
或Model
被接受,但您也可以实现其他接口。
使用您的方法,在进行手动转换之前,您将被限制为Model
另一种接口类型。
假设我有Person
实现方法Walk()
而Model
实现ShowOff()
,以下内容无法直接使用:
newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk
然而,这会:
newPerson().ShowOff()
newPerson().Walk()
答案 3 :(得分:2)
类型T和[] T是不同的类型,它们的方法也是不同的,即使在满足相同的接口时也是如此。 IOW,每个满足模型的类型必须自己实现所有模型的方法 - 方法接收器只能是一种特定类型。
答案 4 :(得分:2)
正如其他人已经回答的那样,[] T是一种独特的类型。我想补充说,可以使用一个简单的实用程序来转换它们。
import "reflect"
// Convert a slice or array of a specific type to array of interface{}
func ToIntf(s interface{}) []interface{} {
v := reflect.ValueOf(s)
// There is no need to check, we want to panic if it's not slice or array
intf := make([]interface{}, v.Len())
for i := 0; i < v.Len(); i++ {
intf[i] = v.Index(i).Interface()
}
return intf
}
现在,您可以像这样使用它:
ToIntf([]int{1,2,3})
答案 5 :(得分:0)
即使Go的实现允许这样做,也很遗憾:您不能将[]Person
分配给类型[]Model
的变量,因为[]Model
具有不同的功能。例如,假设我们也有实现Animal
的{{1}}:
Model
如果我们允许第二行,那么第三行也应该起作用,因为var people []Person = ...
var models []Model = people // not allowed in real Go
models[0] = Animal{..} // ???
var person Person = people[0] // !!!
可以很好地存储models
。第4行仍然应该工作,因为Animal
存储了people
个。但是,最后我们得到一个Person
类型的变量,其中包含一个Person
!
Java实际上允许与第二行相当,这被广泛认为是错误的。 (错误是在运行时捕获的;第3行会抛出Animal
。)