interfaces - golang: slice of struct!=parte de la interfaz que implementa?
golang interfaces (5)
Como Stephen ya respondió la pregunta y eres un principiante, hago hincapié en dar consejos.
Una mejor forma de trabajar con las interfaces de go es no tener un constructor que devuelva la interfaz como podría estar acostumbrado desde otros lenguajes, como java, sino tener un constructor para cada objeto de forma independiente, ya que implementan la interfaz implícitamente.
En lugar de
newModel(type string) Model { ... }
deberías hacer
newPerson() *Person { ... }
newPolitician() *Politician { ... }
con Person
y Politician
ambos implementando los métodos de Model
. Todavía puede usar Person
o Politician
cualquier lugar donde se acepte un Model
, pero también puede implementar otras interfaces.
Con su método, estaría limitado a Model
hasta que realice una conversión manual a otro tipo de interfaz.
Supongamos que tengo una Person
que implementa el método Walk()
y un Model
implementa ShowOff()
, lo siguiente no funcionaría directamente:
newModel("person").ShowOff()
newModel("person").Walk() // Does not compile, Model has no method Walk
Sin embargo esto sería:
newPerson().ShowOff()
newPerson().Walk()
Tengo un Model
interfaz, que es implementado por struct Person
.
Para obtener una instancia modelo, tengo las siguientes funciones auxiliares:
func newModel(c string) Model {
switch c {
case "person":
return newPerson()
}
return nil
}
func newPerson() *Person {
return &Person{}
}
El enfoque anterior me permite devolver una instancia Person debidamente tipada (puede agregar fácilmente nuevos modelos más adelante con el mismo enfoque).
Cuando intenté hacer algo similar para devolver una porción de modelos, recibí un error. Código:
func newModels(c string) []Model {
switch c {
case "person":
return newPersons()
}
return nil
}
func newPersons() *[]Person {
var models []Person
return &models
}
Ir se queja con: cannot use newPersons() (type []Person) as type []Model in return argument
Mi objetivo es devolver una porción del tipo de modelo que se solicite (ya sea []Person
, []FutureModel
, []Terminator2000
, w / e). ¿Qué me estoy perdiendo y cómo puedo implementar correctamente una solución de este tipo?
Como otros ya han respondido, [] T es un tipo distinto. Me gustaría agregar que se puede usar una utilidad simple para convertirlos genéricamente.
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
}
Ahora, puedes usarlo así:
ToIntf([]int{1,2,3})
Esto es muy similar a una pregunta que acabo de responder: https://.com/a/12990540/727643
La respuesta corta es que estás en lo correcto. Una porción de estructuras no es igual a una porción de una interfaz que implementa la estructura.
Una []Person
y un []Model
tienen diferentes diseños de memoria. Esto se debe a que los tipos de los que son segmentos tienen diferentes diseños de memoria. Un Model
es un valor de interfaz que significa que en la memoria tiene dos palabras. Una palabra para la información del tipo, la otra para los datos. Una Person
es una estructura cuyo tamaño depende de los campos que contiene. Para convertir de un []Person
a un []Model
, tendrá que recorrer la matriz y hacer una conversión de tipo para cada elemento.
Como esta conversión es una operación O (n) y daría como resultado la creación de una nueva división, Go se niega a hacerlo implícitamente. Puedes hacerlo explícitamente con el siguiente código.
models := make([]Model, len(persons))
for i, v := range persons {
models[i] = Model(v)
}
return models
Y como señaló dskinner , lo más probable es que desee un trozo de punteros y no un puntero a una porción. Por lo general, no se necesita un puntero a una porción.
*[]Person // pointer to slice
[]*Person // slice of pointers
Los tipos T y [] T son tipos distintos y también son distintos sus métodos, incluso cuando satisfacen la misma interfaz. IOW, cada modelo que satisfaga el modelo debe implementar todos los métodos del Modelo en sí mismo; el receptor de métodos puede ser solo un tipo específico.
Tal vez este es un problema con su tipo de retorno *[]Person
, donde debería ser realmente []*Person
para referenciar que cada índice del sector es una referencia a una Person
, y donde un sector []
es en sí mismo una referencia a una matriz.
Mira el siguiente ejemplo:
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())
}