пытаясь реализовать полиморфизм с интерфейсами Go - PullRequest
0 голосов
/ 24 июня 2018

Я пытаюсь создать слой поверх сторонней библиотеки, в данном случае libchan . Вот интерфейс, который я определил:

type ReceiverStream interface {
    Receive(msg interface{}) error
}

type InboundTransport interface {
    WaitReceiveChannel() (ReceiverStream, error)
}

InboundTransport предназначен для замены типа Transport:

// libchan.go
type Transport interface {
    // NewSendChannel creates and returns a new send channel.  The receive
    // end will get picked up on the remote end of the transport through
    // the remote calling WaitReceiveChannel.
    NewSendChannel() (Sender, error)

    // WaitReceiveChannel waits for a new channel be created by the
    // remote end of the transport calling NewSendChannel.
    WaitReceiveChannel() (Receiver, error)
}

Только для контекста, это определение libchan.Receiver (обратите внимание, что оно соответствует моему ReceiverStream:

// libchan.go
type Receiver interface {
    // Receive receives a message sent across the channel from
    // a sender on the other side of the underlying transport.
    // Receive is expected to receive the same object that was
    // sent by the Sender, any differences between the
    // receive and send type should be handled carefully.  It is
    // up to the application to determine type compatibility, if
    // the receive object is incompatible, Receiver will
    // throw an error.
    Receive(message interface{}) error
}

Transport возвращается библиотекой libchan здесь :

// libchan/session.go:62
func NewTransport(provider StreamProvider) libchan.Transport {
...
}

Так как libchan.Transport и InboundTransport используют метод WaitReceiveChannel() (ReceiverStream, error), я решил, что смогу заменить один на другой, вот так:

func (ln SpdyListener) Accept(addr string) InboundTransport {
    var listener net.Listener
    var err error
    listener, err = net.Listen("tcp", addr)
    if err != nil {
        log.Fatal(err)
    }
    c, err := listener.Accept()
    if err != nil {
        log.Fatal(err)
    }
    p, err := spdy.NewSpdyStreamProvider(c, true)
    if err != nil {
        log.Fatal(err)
    }

    return spdy.NewTransport(p)
}

Но я получаю ошибку:

cannot use spdy.NewTransport(p) (type libchan.Transport) as type InboundTransport in return argument:
    libchan.Transport does not implement InboundTransport (wrong type for WaitReceiveChannel method)
        have WaitReceiveChannel() (libchan.Receiver, error)
        want WaitReceiveChannel() (ReceiverStream, error)

Я предполагаю, что эта ошибка означает, что тип ReceiverStream не соответствует libchan.Receiver, но я подумал, что интерфейсы golang были неявными, что означает, что, пока возвращаемый тип реализует те же методы, что и ожидаемый интерфейс Пройдет компиляция. Могу ли я что-то изменить, чтобы наложить самостоятельно определенный интерфейс на интерфейс, возвращаемый библиотекой третьей части?

TLDR: сторонняя библиотека возвращает объект интерфейса Transport. Интерфейс Transport определяет метод WaitReceiveChannel(). У меня есть собственный интерфейс InboundTransport, который также указывает WaitReceiveChannel(). Сторонний метод, который я вызываю, возвращает объект, который реализует Transport с помощью метода WaitReceiveChannel(). Я предполагал, что он также реализует InboundTransport, поскольку последний также указывает WaitReceiveChannel() того же типа. Это не работает Почему нет?

1 Ответ

0 голосов
/ 24 июня 2018

Как вы уже знаете, интерфейсы в Go удовлетворяются неявно.

Но, как говорится, ошибки

WaitReceiveChannel() (libchan.Receiver, error)

и

WaitReceiveChannel() (ReceiverStream, error)

являются двумя разными типы методов , в результате чего libchan.Transport неявно не реализует InboundTransport.

Чтобы обойти это, вы должны написать обертку вокруг libchan.Transport, которая правильно реализует InboundTransport.

type TransportWrapper struct {
    t *libchan.Transport
}

func (w *TransportWrapper) WaitReceiveChannel() (Receiver, error) {
    return w.t.WaitReceiveChannel()
}

// ...

func (ln SpdyListener) Accept(addr string) InboundTransport {
    var listener net.Listener
    var err error
    listener, err = net.Listen("tcp", addr)
    if err != nil {
        log.Fatal(err)
    }
    c, err := listener.Accept()
    if err != nil {
        log.Fatal(err)
    }
    p, err := spdy.NewSpdyStreamProvider(c, true)
    if err != nil {
        log.Fatal(err)
    }

    return &TransportWrapper{spdy.NewTransport(p)}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...