Golang 并没有类继承的机制,但是有类似的方法可以实现 struct 方法的覆盖。
比如下面这段 Sarama 库中的代码:
type Client interface {
Broker(brokerID int32) (*Broker, error)
Close() error
// 省略掉大量无关的函数定义
}
// nopCloserClient embeds an existing Client, but disables
// the Close method (yet all other methods pass
// through unchanged). This is for use in larger structs
// where it is undesirable to close the client that was
// passed in by the caller.
type nopCloserClient struct {
Client
}
// Close intercepts and purposely does not call the underlying
// client's Close() method.
func (ncc *nopCloserClient) Close() error {
return nil
}
这段代码的核心在于把一个 Client 的 interface 变量作为 nopCloserClient 的对象。nopCloserClient
的对象仍然被认为是个 Client,是可以覆值给 Client 的 interface 的。
// NewConsumerFromClient creates a new consumer using the given client. It is still
// necessary to call Close() on the underlying client when shutting down this consumer.
func NewConsumerFromClient(client Client) (Consumer, error) {
// For clients passed in by the client, ensure we don't
// call Close() on it.
cli := &nopCloserClient{client}
return newConsumer(cli)
}
Client
是个带 Close
函数的 interface。nopCloserClient
“继承” 了 Client
接口,但是通过编写额外的 Close
函数使得其没有效果。
再写一段简单的代码演示之:
package main
import (
"fmt"
)
type Client interface {
Close()
}
type NormalClient struct {
}
func (c NormalClient) Close() {
fmt.Println("Closing")
}
type NopClient struct {
Client
}
func (c NopClient) Close() {
fmt.Println("Do nothing")
}
func main() {
normal := NormalClient{}
nop := NopClient{Client: normal}
nop.Close() // => "Do nothing"
nop.Client.Close() // => "Closing"
// 定义一个 client 的 interface 值,nop 可以被赋值给 client
var client Client = nop
client.Close() // => "Do nothing"
}
本质上,Golang 通过「一个 struct 可以有一或多个匿名 interface field」,来实现了多 组合继承 的效果、以及 可以覆盖“父类”的实现。所谓 组合继承 是我编的一个概念:
- 对于一般编程语言,继承的是具体的类,比如
Triangle
类继承了Shape
类 - 对于 Go 而言,继承的实际上是接口而不是类。比如
FileReader
如果继承了io.Closer
接口,那么视具体的fileReader
对象中的io.Closer
interface 值底下的具体类型,可以是ACloser
也可以是BCloser
;这时调用fileReader.Close()
就会调用到具体的ACloser.Close()
或是BCloser.Close()
,非常灵活