Go嵌套:接口嵌套接口

创建于 2023年12月15日修改于 2024年5月5日
GoEmbedding

Go嵌套


原文:https://eli.thegreenplace.net/2020/embedding-in-go-part-2-interfaces-in-interfaces/
翻译:literank.cn

接口中嵌套接口

在 Go 中,将一个接口嵌套到另一个接口中是最简单的嵌套方式,因为接口仅声明能力,不实际定义类型的数据和行为。

让我们从 Effective Go 中的例子开始。 它展示了 Go 标准库中接口嵌套的一个知名案例。给定 io.Readerio.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.ReadCloserio.WriteCloserio.ReadWriteCloserio.ReadSeekerio.WriteSeekerio.ReadWriteSeeker 等等。 都用上述方法区声明的话,单单 Read 方法的声明就可能需要重复10次以上,这是无法接受的。幸运的是,接口嵌套提供了完美的解决方案:

type ReadWriter interface {
    Reader
    Writer
}

除了防止重复外,这个声明还以最清晰的方式表明了意图:要实现 ReadWriter,你必须实现 ReaderWriter

重叠方法的修复

给定接口 ABCD,其定义如下:

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.ReadCloserio.WriteCloserClose() 方法会引发重复冲突。

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