El ejemplo específico está aquí: https://play.golang.org/p/ZNmviKWLwe

Tengo una interfaz A. Espero que todas las implementaciones de A tengan una determinada firma (de ahí la interfaz). Espero que uno de esos devuelva algo útil que implemente otra interfaz.

type Box interface {
    GetToy(int) (*Toy, error)
}
type Toy interface{
    Play()
}


type CarBox struct {
   Length int
   Width int
   Height int
   toys []*Matchbox
}
type Matchbox struct {}

func (b *CarBox) Size () int {
    return b.Length*b.Width*b.Height
}
func (b *CarBox) GetToy(j int) (*Matchbox, error) {
    return b.toys[j], nil
}
func (m *Matchbox) Play() {
    fmt.Println("Zoom!")
}

Pero, por supuesto, no funciona, porque GetToy() devuelve *Matchbox y no *Toy, aunque Matchbox es una implementación perfectamente aceptable de Toy. Y no, no es por los consejos; da el mismo resultado si devuelvo *Toy en GetToy o simplemente Toy:

tmp/sandbox721971102/main.go:33:15: cannot use CarBox literal (type CarBox) as type Box in return argument:
    CarBox does not implement Box (wrong type for GetToy method)
        have GetToy(int) (*Matchbox, error)
        want GetToy(int) (*Toy, error)

Obviamente, mi patrón no coincide con el funcionamiento de go. ¿Cuál es la forma "correcta" de hacer esto?

go
2
deitch 12 nov. 2017 a las 10:54

2 respuestas

La mejor respuesta

En primer lugar, casi nunca necesita un puntero a un tipo de interfaz. Entonces, la firma del método GetToy debería cambiarse a:

type Box interface {
    GetToy(int) (Toy, error) // use Toy, not *Toy
}

Ahora, tenga en cuenta que los tipos satisfacen las interfaces implícitamente. Como resultado, al implementar el método Play(), *Matchbox satisface implícitamente la interfaz Toy (tenga en cuenta que *, el tipo que implementa la interfaz Toy es puntero a Matchbox y no a Matchbox ).

Esto significa que un valor de tipo *Matchbox se puede sustituir por el tipo Toy. Entonces, lo que queda es arreglar el método GetToy definido en *CarBox:

func (b *CarBox) GetToy(j int) (Toy, error) {
    return b.toys[j], nil
}

Ahora GetToy anterior devolverá un *Matchbox que implementa la interfaz Toy.

5
abhink 12 nov. 2017 a las 08:09

Algunas correcciones: https://play.golang.org/p/-mDr5SDZvV

El puntero a la interfaz es algo extremadamente necesario desde atrás. Utilice la interfaz: puede contener un tipo concreto.

ACTUALIZACIÓN : Si desea tener un método con un receptor de puntero como func (b *CarBox) GetToy(j int), puede devolver el puntero desde la función Tester (preste atención a & ):

func Tester() Box {
    return &CarBox{}
}

De esta forma, el valor de la interfaz devuelto por Tester concluirá con un puntero a la instancia CarBox. Por lo tanto, Go runtime podrá ejecutar métodos con un receptor de puntero. De lo contrario, solo habría un valor, no un puntero. Por lo tanto, solo se pueden llamar métodos con receptores de valor.

1
Eugene Lisitsky 12 nov. 2017 a las 10:19