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
,而从非嵌套的版本中比对判断这些信息则要困难得多。