Пытаясь реализовать полиморфизм с интерфейсами Go
Я пытаюсь создать слой поверх сторонней библиотеки, в данном случае 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 ответ
Как вы уже знаете, интерфейсы в 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)}
}