golang:slice of slice!=它实现的接口切片?

Jon*_* L. 55 go

我有一个接口Model,由struct实现Person.

要获取模型实例,我有以下帮助函数:

func newModel(c string) Model {
    switch c {
    case "person":
        return newPerson()
    }
    return nil
}

func newPerson() *Person {
    return &Person{}
}
Run Code Online (Sandbox Code Playgroud)

上面的方法允许我返回一个正确类型的Person实例(稍后可以使用相同的方法轻松添加新模型).

当我尝试做类似的事情来返回一片模型时,我得到一个错误.码:

func newModels(c string) []Model {
    switch c {
    case "person":
        return newPersons()
    }
    return nil
}

func newPersons() *[]Person {
    var models []Person
    return &models
}
Run Code Online (Sandbox Code Playgroud)

去投诉: cannot use newPersons() (type []Person) as type []Model in return argument

我的目标是回到请求任何机型的切片(是否[]Person,[]FutureModel,[]Terminator2000,W/E).我缺少什么,我该如何正确实施这样的解决方案?

Ste*_*erg 86

这与我刚才回答的问题非常类似:https://stackoverflow.com/a/12990540/727643

简短的回答是你是对的.一块结构不等于结构实现的接口的一部分.

A []Person和a []Model具有不同的内存布局.这是因为它们是切片的类型具有不同的内存布局.A Model是一个接口值,这意味着在内存中它的大小是两个字.一个字为类型信息,另一个为数据.A Person是一个结构,其大小取决于它包含的字段.要从a转换[]Person为a []Model,您需要遍历数组并为每个元素执行类型转换.

由于此转换是O(n)操作并且将导致创建新切片,因此Go拒绝隐式执行此操作.您可以使用以下代码显式执行此操作.

models := make([]Model, len(persons))
for i, v := range persons {
    models[i] = Model(v)
}
return models
Run Code Online (Sandbox Code Playgroud)

正如dskinner指出的那样,你很可能想要一片指针,而不是指向切片的指针.通常不需要指向切片的指针.

*[]Person        // pointer to slice
[]*Person        // slice of pointers
Run Code Online (Sandbox Code Playgroud)

  • 那么如何以抽象、通用的方式写一些东西呢?想象一下,您有来自数据库查询的 100k 个结果,并且您必须迭代所有这些结果并创建每个数据集的副本。这意味着 Go 中不能有通用接口 (3认同)

dsk*_*ner 8

也许这是你的返回类型的一个问题,*[]Person实际应该[]*Person如此引用切片的每个索引是对a的引用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())
}
Run Code Online (Sandbox Code Playgroud)


nem*_*emo 7

由于斯蒂芬已经回答了这个问题并且你是初学者,我强调提出建议.

使用go的接口的一种更好的方法是不要让构造函数返回接口,就像你可能习惯于从其他语言(如java)那样,而是为每个对象独立地构造一个,因为它们隐式地实现了接口.

代替

newModel(type string) Model { ... }
Run Code Online (Sandbox Code Playgroud)

你应该做

newPerson() *Person { ... }
newPolitician() *Politician { ... }
Run Code Online (Sandbox Code Playgroud)

PersonPolitician实施的方法Model.您仍然可以使用PersonPolitician在任何Model 接受的地方使用,但您也可以实现其他接口.

使用您的方法,您将被限制为Model直到您手动转换为另一种接口类型.

假设我有一个Person它实现了方法Walk()Model工具ShowOff(),下面就不会直截了当的工作:

newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk
Run Code Online (Sandbox Code Playgroud)

但是这会:

newPerson().ShowOff()
newPerson().Walk()
Run Code Online (Sandbox Code Playgroud)

  • 即使返回`interface {}`,也始终保留对类型的了解,问题是您需要执行运行时类型断言而不是编译器类型检查.没有运行时断言的`name - > object`的通用解决方案只能使用泛型,这是不支持的.因此,如果您这样做,您必须忍受反思或解决方案的缺点. (2认同)