ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
## 5.12\. 内置 Go 不提供通行的、类型驱动的子类划分的概念,但它通过在结构或界面内置,确实有能力从某实现“借”些片段。 界面内置非常简单。我们提到过 io.Reader 和 io.Writer 的界面;这里是其定义: ``` type Reader interface { Read(p []byte) (n int, err os.Error) } type Writer interface { Write(p []byte) (n int, err os.Error) } ``` io 包也导出一些其它的界面来规范实现其多个方法的物件。例如,io.ReadWriter 界面同时包括 Read 和 Write 。我们可以明确的列出这两个方法来规定 io.ReadWriter ,但更简单更有启发的是内置这两个界面形成新界面,如下: ``` // ReadWrite is the interface that groups the basic Read and Write methods. type ReadWriter interface { Reader Writer } ``` 顾名思意,ReadWriter 可做 Reader 和 Writer 两个的事;它是内置界面的集合(必须是没有交集的方法)。只有界面可以内置在界面里。 同样的基本概念适用于结构,但牵连更广。bu?o 包有两个结构类型,bu?o.Reader 和 bu?o.Writer ,每个都当然对应实现着 io 包的界面。并且,bu?o 也实现了缓冲的读写,这是通过把 reader 和 writer 内置到一个结构做到的;它列出类型但不给名称: ``` // ReadWriter stores pointers to a Reader and a Writer. // It implements io.ReadWriter. type ReadWriter struct { *Reader // *bufio.Reader *Writer // *bufio.Writer } ``` 内置元素是指针所以使用前必须初始化指向有效的结构。 ReadWriter 结构可以写为: ``` type ReadWriter struct { reader *Reader writer *Writer } ``` 但要提升域的方法又要满足 io 界面,我们还需提供转发方法,如: ``` func (rw *ReadWriter) Read(p []byte) (n int, err os.Error) { return rw.reader.Read(p) } ``` 通过直接内置结构,我们避免了这些账目管理。内置类型的方法是附送的,亦即 bu?o.ReadWriter 除了有 bu?o.Reader 和bu?o.Writer 的方法,还同时满足三个界面:io.Reader,io.Writer 和 io.ReadWriter 。 内置和子类划分有着重要不同。我们内置类型时,此类的方法成为外层类型的方法,但调用时其接受者是内层类型,而不是外层。我们的例子里,当 bu?o.ReadWriter 的 Read 方法被调用时,它的效果和上面所写的转发方法完全一样;接受者是 ReadWriter 的 reader,而不是 ReadWriter 自身。 内置也用来提供某种便利。下例是一个内置域和普通的,带名的域在一起: ``` type Job struct { Command string *log.Logger } ``` 此时 Job 类型有 Log,Logf 和其它 *log.Logger 的方法。 我们当然可以给 Logger 个名字,但无此必要。这里,初始化后,我们可以 log Job: ``` job.Log("starting now...") ``` Logger 是结构的普通域所以我们能用通常的架构函数初始化它: ``` func NewJob(command string, logger *log.Logger) *Job { return &Job{command, logger} } ``` 或使用组合字面: ``` job := &Job{command, log.New(os.Stderr, nil, "Job: ", log.Ldate)} ``` 如果我们需要直接引用内置域,域的类型名,忽略包标识,可作为域名。如果我们要得到 Job 变量 job 的 *log.Logger,我们用job.Logger 。这可用在细化 Logger 的方法上: ``` func (job *Job) Logf(format string, args ...) { job.Logger.Logf("%q: %s", job.Command, fmt.Sprintf(format, args)) } ``` 内置类型导致撞名的问题,但解决方案很简单。首先,域或方法 X 隐藏此类型更深层部分的 X 项。如果 log.Logger 包括叫 Command 的域或方法,则 Job 的 Command 域占主导权。 其次,如果同层出现同名,通常是个错误;如果 Job 结构有另一域或方法叫 Logger,要内置 log.Logger 会出错。但只要重名在类型定义外的程序中不被提及,就不成问题。此条件提供了改动外部的内置类型的某种保护;只要两个域都没被用到,新增的域名和另一子类的域重名也没有问题。