Go嵌套:接口嵌套接口
原文:https://eli.thegreenplace.net/2020/embedding-in-go-part-2-interfaces-in-interfaces/
翻译:literank.cn
接口中嵌套接口
在 Go 中,将一个接口嵌套到另一个接口中是最简单的嵌套方式,因为接口仅声明能力,不实际定义类型的数据和行为。
让我们从 Effective Go 中的例子开始。
它展示了 Go 标准库中接口嵌套的一个知名案例。给定 io.Reader 和 io.Writer 接口:
type Reader interface {
Read(p []byte) (n int, err error)
}
type Writer interface {
Write(p []byte) (n int, err error)
}
我们如何定义一个既是读取器又是写入器类型的接口?一个可行的方法是:
type ReadWriter interface {
Read(p []byte) (n int, err error)
Write(p []byte) (n int, err error)
}
但是这个方法除了在多个地方重复相同的方法声明外,可读性也不好。因为很难看出 ReadWriter 接口与其他两个接口组合是如何结合的。开发者要么需要记住每个方法的确切声明,要么需要不断地跟其他接口比对。
注意,标准库中有许多这样的组合而成的接口:io.ReadCloser、io.WriteCloser、io.ReadWriteCloser、io.ReadSeeker、io.WriteSeeker、io.ReadWriteSeeker 等等。
都用上述方法区声明的话,单单 Read 方法的声明就可能需要重复10次以上,这是无法接受的。幸运的是,接口嵌套提供了完美的解决方案:
type ReadWriter interface {
Reader
Writer
}
除了防止重复外,这个声明还以最清晰的方式表明了意图:要实现 ReadWriter,你必须实现 Reader 和 Writer。
重叠方法的修复
给定接口 A、B、C 和 D,其定义如下:
type A interface {
Amethod()
}
type B interface {
A
Bmethod()
}
type C interface {
Cmethod()
}
type D interface {
B
C
Dmethod()
}
D 的方法集将包含 Amethod()、Bmethod()、Cmethod() 和 Dmethod()。
然而,假设 C 被定义为:
type C interface {
A
Cmethod()
}
一般来说,这不应该改变 D 的方法集。然而,在 Go 1.14 之前,这将导致 D 出现错误“Duplicate method Amethod”,因为 Amethod() 在 B 嵌套和 C 嵌套声明里声明了两次。
Go 1.14 修复了这个问题,D 的方法集是它嵌套的接口的方法集和它自己的方法的并集。
一个更真实的例子来自标准库。io.ReadWriteCloser 类型被定义为:
type ReadWriteCloser interface {
Reader
Writer
Closer
}
但它可以更简洁地定义为:
type ReadWriteCloser interface {
io.ReadCloser
io.WriteCloser
}
在 Go 1.14 之前这是不可能的,因为来自 io.ReadCloser 和 io.WriteCloser 的 Close() 方法会引发重复冲突。
示例:net.Error
net 包有自己的错误接口,如下所示:
// An Error represents a network error.
type Error interface {
error
Timeout() bool // 是否为超时错误?
Temporary() bool // 是否为临时错误?
}
注意 Go 语言内置的 error 接口的嵌套。这种嵌套非常清晰地声明了意图:net.Error 也是一个 error。
读者想知道是否可将其视为标准错误时可立即直观地得到答案,而不必查找 Error() 方法的声明并将其与标准错误 error 的规范进行比较。
示例:heap.Interface
heap 包为使用者提供了以下需实现的接口:
type Interface interface {
sort.Interface
Push(x interface{}) // 将 x 添加为元素第 Len() 个元素
Pop() interface{} // 删除并返回在 Len() - 1 处的元素
}
所有实现 heap.Interface 的类型必须实现 sort.Interface;后者需要3个方法,因此在没有嵌套的情况下编写 heap.Interface 会像这样:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
Push(x interface{})
Pop() interface{}
}
嵌套的版本显然更简更优。最重要的是,它非常清楚地表明类型必须先实现 sort.Interface,而从非嵌套的版本中比对判断这些信息则要困难得多。